Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This course is archived!
This tutorial uses an older version of Symfony of the stripe-php SDK. The majority of the concepts are still valid, though there *are* differences. We've done our best to add notes & comments that describe these changes.

Force HTTPS ... please

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $9.00

Guess what? You could deploy this code right now. Sure - we have a lot more to talk about - like subscriptions & discounts - but the system is ready.

Oh, but there's just one thing that you cannot forget to do. And that's to force https to be used on your checkout page.

Right now, there is no little lock icon in my browser - this page is not secure. Of course it's not a problem right now because I'm just coding locally.

But on production, different story. Even though you're not handling credit card information, Stripe does submit that token to our server. If that submit happens over a non-https connection, that's a security risk: there could be somebody in the middle reading that token. Regardless of what they might or might not be able to do with that, we need to avoid this.

There are a lot of ways to force HTTPs, but let me show you my favorite in Symfony. In OrderController, right above checkoutAction(), this @Route annotation is what defines the URL to this page. At the end of this, add a new option called schemes set to two curly braces and a set of double-quotes with https inside:

... lines 1 - 11
class OrderController extends BaseController
{
... lines 14 - 27
/**
* @Route("/checkout", name="order_checkout", schemes={"https"})
... line 30
*/
public function checkoutAction(Request $request)
{
... lines 34 - 68
}
}

OK, go back and refresh! Cool! Symfony automatically redirects me to https. Life is good.

No HTTPS in dev Please!

Wait, life is not good. I hate needing to setup SSL certificates on my local machine. I actually have one setup already, but other developers might not. That's a huge pain for them... for no benefit.

Fortunately, there's a trick. Replace https, with %secure_channel%:

... lines 1 - 11
class OrderController extends BaseController
{
... lines 14 - 27
/**
* @Route("/checkout", name="order_checkout", schemes={"%secure_channel%"})
... line 30
*/
public function checkoutAction(Request $request)
{
... lines 34 - 68
}
}

This syntax is referencing a parameter in Symfony, so basically a configuration variable. Open parameters.yml, add a new secure_channel parameter and set it to http:

... lines 1 - 3
parameters:
... lines 5 - 22
# set to https on production
secure_channel: http

And as you know, if you add a key here, also add it to parameters.yml.dist:

... lines 1 - 3
parameters:
... lines 5 - 22
# set to https on production
secure_channel: http

Ok, head back to the homepage: http://localhost:8000 and click to checkout. Hey! We're back in http. When you deploy, change that setting to https and boom, your checkout will be secure.

So there's your little trick for forcing https without being forced to hate your life while coding.

Leave a comment!

13
Login or Register to join the conversation
Default user avatar
Default user avatar Blueblazer172 | posted 5 years ago

I'm back :)

I have the exact code as ryan, but when i click on checkout. this error occurs:

No route found for "GET /%secure_channel%://localhost/checkout" (from "http://localhost:8000/")

but when i type localhost:8000/checkout and hit enter is works.

has to be an easy problem :)

Reply

Yo man,

Hm, looks like Symfony can't resolve its value. Have you add the "secure_channel" parameter in your app/config/parameters.yml.dist and *also*, what more important, in app/config/parameters.yml file? Also, please, try to clear the cache manually for both dev/prod environments.

Cheers!

Reply
Default user avatar
Default user avatar Blueblazer172 | Victor | posted 5 years ago

i did everything but it wont work :/

Reply

Hey Blueblazer172!

Hmm, so I just re-checked the code - pulled the code down to this step, and tried it again. It does work fine for me. So, a few things to try out :)

1) If you run php bin/console debug:router order_checkout , what is the value next to Scheme?

2) If you remove the "secure_channel" from parameters.yml, do you get an error (you should, you should get an error that the parameter secure_channel must be defined). If you don't, it definitely makes me think that - for some reason - the parameter isn't being used.

3) Could you paste your @Route + controller code here?

Cheers!

Reply
Default user avatar
Default user avatar Blueblazer172 | weaverryan | posted 5 years ago

1) http
2) i dont see any error...
3) http://imgur.com/a/pxJfg

Reply

Hey Blueblazer172 !

Sorry for my late reply! So, there are two weird things happening, and they seem to conflict with each other:

A) because you remove secure_channel from parameters.yml and do NOT see any error, it means that Symfony does not see %secure_channel% referenced anywhere. In some ways, that's no surprise - it seems like Symfony is not replacing this value for some reason.

B) But, the fact that the "Scheme" when you run bin/console debug:router order_checkout says HTTP is puzzling. By default (if you don't set schemes), this will say "All". Since it says "http", it means that this value IS being set.

So, it's a mystery. It seems to be set in some cases, but not others - and that's just not how Symfony's routing system works: one set of routes are compiled and used everywhere. Is it possible that you have a second route defined accidentally with the same name - order_checkout? Also, you mentioned on your first post that when you click "Checkout", you get the wrong behavior, but when you type http:/localhost:8000/checkout and hit enter, it works as expected. Can you post the code for your link? The code for your controller looks perfect. If you change the "schemes" foryour @Route to "https" and run bin/console debug:router order_checkout, does the Schemes value change?

I'm sure it's something small. This behavior does work - it must be some small configuration we're missing!

Cheers!

Reply
Default user avatar
Default user avatar Blueblazer172 | weaverryan | posted 5 years ago

Hi Ryan,

thanks for taking your time on solving this issue.

I have no other route with the same name :)

I have put all files in this github project maybe you can find the issue there
https://github.com/Blueblaz...

And yes it changes to https if i change it :)

So what I think is that there must be something wrong with my @Route or generally with the Controller or the twig file

Regards
Hannes

Reply

Hi Hannes!

Ah, thank you! Full code - that is the BEST way to help debug. And, I found the problem thanks to it! It's actually a big in Symfony (https://github.com/symfony/... which has a pending pull request to fix (https://github.com/symfony/....

Here's the situation:

A) Whenever you change a routing file (or controller with annotations), Symfony is smart enough to know that it needs to rebuild the routing cache.

B) But, when you change the parameters.yml (e.g. to change from http to https or vice versa), the routing cache is NOT rebuilt. In other words, when you change your parameter from http to https, the old routing cached (the one that thinks the scheme is set to http) is still set.

This problem only affects the dev environment. And the fix even in dev is easy - either clear your cache or simply modify any line in your controller file and then refresh. This will trigger your route cache to be rebuilt.

It's not often you can find a Symfony bug, so... congrats?? :). I've just commented on the pull request to fix it - to see if we can get a version of it merged.

Cheers!

Reply

And, the bug in Symfony has been fixed! https://github.com/symfony/...

Viva the community :)

Reply
Dmitriy Avatar
Dmitriy Avatar Dmitriy | posted 5 years ago

Help, how can I implement a similar action on Symfony 4?

Reply

Hey Dmitriy,

Actually, almost the same way. But instead of setting this parameter in parameters.yaml.dist and parameters.yaml files put it in config/services/yaml under "parameters:" key. Well, in Symfony 4 you still can create the same plain variables under the "parameters:" key. But if you want to make its value server-specific, set the value of the parameter with "%env('SECURE_CHANNEL')%" and declare this env var in your .env and .env.dist files.

So the main idea is to get the proper value from an environment variable, and set the value to a parameter which you'll be able to use in @Route() annotation.

Cheers!

Reply
Dmitriy Avatar
Dmitriy Avatar Dmitriy | Victor | posted 5 years ago | edited

Thank you, Victor.

I solved the problem with this code inside the public/.htaccess file


RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Now all requests on http are redirected to https.

Reply

Great! And actually doing it on the server's level is even better I think. Thanks for sharing it with others!

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony of the stripe-php SDK. The majority of the concepts are still valid, though there *are* differences. We've done our best to add notes & comments that describe these changes.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9, <7.4",
        "symfony/symfony": "3.1.*", // v3.1.10
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "~2.0.1", // v2.0.1
        "stof/doctrine-extensions-bundle": "^1.2", // v1.2.2
        "stripe/stripe-php": "^3.15", // v3.23.0
        "doctrine/doctrine-migrations-bundle": "^1.1", // 1.1.1
        "twig/twig": "^1.24.1", // v1.35.2
        "composer/package-versions-deprecated": "^1.11" // 1.11.99
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.2
        "hautelook/alice-bundle": "^1.3", // v1.3.1
        "doctrine/data-fixtures": "^1.2" // v1.2.1
    }
}
userVoice