55

Edit

I can't really find a way to generate a secure URL from route name.

To get a full URL, I use

echo route('my_route_name');

But what to do, if I want a URL with https?

Jinal Somaiya
  • 1,931
  • 15
  • 29
Mārtiņš Briedis
  • 17,396
  • 5
  • 54
  • 76
  • 1
    Does this answer your question? [How to force Laravel Project to use HTTPS for all routes?](https://stackoverflow.com/questions/35827062/how-to-force-laravel-project-to-use-https-for-all-routes) – N69S Jun 29 '21 at 10:33

17 Answers17

91

UPDATE: As pointed out in the comments, a simpler way of doing this would be adding URL::forceSchema('https'); for Laravel version between 4.2-5.3 or URL::forceScheme('https'); for version 5.4+ in the boot method of your AppServiceProvider file.

Old answer:

It's actually entirely possible and there's only one line of code needed to accomplish that.

Laravel doesn't check the presence of SSL by itself, it depends on Symfony. And there goes our key to making it believe that the current request is secure.

The thing is, we have to set the HTTPS server param to true and the easiest method is to paste the following code in the boot method of your AppServiceProvider:

$this->app['request']->server->set('HTTPS', true);

In my very case, I only need to force SSL in production, the local env should still work on http. This is how I force SSL only on production:

$this->app['request']->server->set('HTTPS', $this->app->environment() != 'local');

By the way, mind those terms, you may need them in the future.

Sergiu Sandrean
  • 521
  • 5
  • 11
Ionut Bajescu
  • 1,243
  • 1
  • 11
  • 9
  • 2
    This will actually trick the whole environment to think that the request came from https, which can be a security concern. I would recommend forcing https via `URL::forceSchema('https')` instead. – Unnawut Aug 29 '16 at 11:41
  • 12
    UPDATE: Laravel 5+ - Url::forceSchema changed to Url::forceScheme – Anthony Harley May 10 '17 at 22:55
  • 2
    This worked but for those who miss it at first like me, this is also needed at the top of the file: `use Illuminate\Support\Facades\URL;` – Jordan Mar 16 '19 at 20:25
27

Laravel 8

I recently resolved this by modifying this file:

app/Providers/AppServiceProvider.php

in the method boot() add the following:

URL::forceScheme('https');

Add the use in the top:

use Illuminate\Support\Facades\URL;

to work in your local environment you can leave it like this:

public function boot()
{
    if(env('APP_ENV') !== 'local') {
        URL::forceScheme('https');
    }
}

Note: Don't forget to set your env variable APP_ENV with prod for the production file.

APP_ENV=prod
Oscar Gallardo
  • 2,240
  • 3
  • 27
  • 47
15

Actually turns out, that laravel doesn't care if url is secure or not, because it generates based on the current url. If you're on https page, route() will return secure url. If on http, then http:// url

The problem was, that Laravel didn't detect that https was enabled, which was due to faulty server configuration.

You can check if Laravel sees the current connection as https by calling Request::isSecure();

Mārtiņš Briedis
  • 17,396
  • 5
  • 54
  • 76
  • 34
    For those who are using Laravel behind a load balancer or other proxy, `Request::isSecure()` will always return false, unless you trust the `X-Forwarded-Proto` header from your proxy server. See: [Laravel Tricks - Fix SSL in Laravel 4+](http://laravel-tricks.com/tricks/fix-ssl-in-laravel-4-when-server-is-behind-a-load-balancer-or-a-reverse-proxy) – Adam Link Mar 23 '15 at 18:48
  • 9
    @AdamLink God damn it, thank you so much. I searched for hours for this. I shall add some Google Keywords now in this comment: Laravel http https nginx reverse proxy, laravel route relative url https. – Henrik Jun 19 '16 at 10:57
  • In my experience using Laravel 5.2 on XAMPP/Windows does not generate https URL's with `route()` if the request uses https. I noticed there's no `HTTPS` entry in `$_SERVER` so the protocol is not correctly detected. – Kwebble Nov 25 '16 at 15:14
  • Laravel 5.3 does not detect HTTPS even in Linux. – Dissident Rage Dec 21 '16 at 20:16
  • @DissidentRage probably your webserver is configured incorreclty. Check if $_SERVER has a field that indicates if https is on. – Mārtiņš Briedis Feb 06 '17 at 13:19
  • It doesn't work if I use ``route`` on email template. – tirta keniten Apr 13 '18 at 09:07
  • 1
    @tirtakeniten check your .env file, what is the domain URL specified? It should be with https, not http – Mārtiņš Briedis Apr 13 '18 at 23:06
  • @Mārtiņš Briedis It worked, just a stupid code that I missed. Thank you – tirta keniten Apr 16 '18 at 08:32
13

As I mentioned in a relevant question, I found 5 ways of how to generate secure URLs.

  1. Configure your web server to redirect all non-secure requests to https. Example of a nginx config:

    server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name example.com www.example.com;
        return 301 https://example.com$request_uri;
    }
    
  2. Set your environment variable APP_URL using https:

    APP_URL=https://example.com
    
  3. Use helper secure_url() (Laravel5.6)

  4. Add following string to AppServiceProvider::boot() method (for version 5.4+):

    \Illuminate\Support\Facades\URL::forceScheme('https');
    
  5. Implicitly set scheme for route group (Laravel5.6):

    Route::group(['scheme' => 'https'], function () {
        // Route::get(...)->name(...);
    });
    

    At the moment this way is not documented, but it works well.

Prisacari Dmitrii
  • 1,985
  • 1
  • 23
  • 33
  • 2
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/20322282) – petezurich Jul 17 '18 at 12:00
  • 2
    @peterzurich Fixed with taking into account your comment – Prisacari Dmitrii Jul 17 '18 at 12:50
8

I think there is only one way to do this.

To generate the secure URL to your named routes, you might want to pass in your route into the secure_url helper function.

secure_url(URL::route('your_route_name', [], false));

You can't really use the route helper function because it generates absolute URL (with http://) by default and it's http not the https version that you wanted

  • 1
    Do you have an idea, how to get https when redirecting from controller? `return Redirect::route('login')` I'm amazed how difficult this is :) – Mārtiņš Briedis Jun 26 '14 at 11:07
  • 1
    I think it's pretty much similar to the above method except that you might want to use `Redirect::to` and pass in the above code as the parameter. –  Jun 26 '14 at 11:17
  • That works, but a better solution would be to use the actual response object returned by the Redirect::route call and modify it to make it secure. – Benubird Sep 12 '14 at 10:43
8

Laravel 5.x will generate secure URL via route() helper if it detects the incoming connection is secure. Problem usually happen if the app is hidden behind load balancer or proxy (e.g. Cloudflare) since the connection between app server and load balancer/proxy might not be secure.

I am using Laravel Forge + Cloudflare now and this is the easiest way I could find to enable app thinking incoming connection is secure (not sure about other proxy).

  1. Generate self signed certificate (see https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs or http://www.selfsignedcertificate.com/)

  2. In Forge panel, insert your private key and cert via Sites > your-site > SSL Certificates > Install Existing Certificate.

  3. Activate

  4. In CloudFlare panel, Crypto > SSL, choose “Full” (not strict)

  5. Done (it will take few minutes for the change to get propagated)

In short, connection between client and Cloudflare is secured by Cloudflare's own SSL. Connection between app server and Cloudflare is protected via your generated cert (thus the app is seeing 'connection' as secure.

You can apply the same principle with other stacks.

Aditya
  • 757
  • 8
  • 11
6

Use secure_url:

secure_url(URL::route('your_route_name', [], false));

You will need to set URL::route to false in order to not return a full URL. Then use secure_url function generates a fully qualified HTTPS URL to the given path.

From the UrlGenerator interface you can use URL::route

string route(string $name, mixed $parameters = array(), bool $absolute = true)

Get the URL to a named route.
Parameters
string  $name   
mixed   $parameters     
bool    $absolute   
Return Value
string

https://laravel.com/api/5.4/Illuminate/Contracts/Routing/UrlGenerator.html

PHPGrandMaster
  • 358
  • 2
  • 10
4

In most cases routes should be generated with the same scheme your site was loaded with. Laravel automatically detects if request has X-Forwarded-Proto header and uses it to decide which scheme to use in generated route URLs. If your site is behind reverse proxy then you should add reverse proxy IP address to list of trusted proxies. https://github.com/fideloper/TrustedProxy package helps to do this. It's included in Laravel 5.5. For example, my config/trustedproxy.php looks like:

<?php

return [
    'proxies' => '*',

    'headers' => [
    ]
];

I use it with nginx reverse proxy that has the following configuration:

server {
  listen 80;
  server_name example.com; 
  access_log  /var/log/nginx/example.com_access.log;
  error_log   /var/log/nginx/example.com_error.log;

  client_max_body_size 50m;

  location / {
    proxy_pass              http://localhost:8002;
    proxy_set_header        Host $host;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
  }

  listen 443 ssl;
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
  include /etc/letsencrypt/options-ssl-nginx.conf;
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;


  if ($scheme != "https") {
    return 301 https://$host$request_uri;
  }
}

Replace example.com with your domain. SSL certificates was provided by Let's Encrypt with certbot.

mixel
  • 25,177
  • 13
  • 126
  • 165
4

On laravel 5.5.* You only need to add https on your .env file as AppServiceProvider already had function that checks if your APP_URL or app.url on your config has https on it.

class AppServiceProvider extends ServiceProvider
{
 public function boot()
 {

    \URL::forceRootUrl(\Config::get('app.url'));    
    if (str_contains(\Config::get('app.url'), 'https://')) {
        \URL::forceScheme('https');
    }
 }
2

This is certainly old, but someone like me will dump over here one day.

In your .env file define the APP_URL to use https instead of using http. Because all laravel url are generated based on this variable.

    APP_URL=https://example.com

and wherever you want you can just say

{{ URL::route('my.route', params) }}

Or

{{ route('my.route', params) }}

With make sure all the routes are generated with secure protocol, add in the boot method of AppServiceProvider class:

<?php

namespace App\Providers;

use Illuminate\Routing\UrlGenerator;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot(UrlGenerator $url)
    {
        if (config('app.production')) {
            $url->forceScheme('https');
        }
    }


Samuel Bié
  • 783
  • 8
  • 11
1

Just add your application domain with the https protocol in the APP_URL of your .env file.

APP_URL=https://example.com

Then run route:cache

Meneses
  • 56
  • 5
0

For reference of future visitors:

The secure_url function doesn't correctly handle GET parameters. So, for example, if you want to convert the url that the user has visited into a secure url while retaining the GET fields, you need to use this:

secure_url(Request::path()).'?'.http_build_query(Input::all());

Particularly note the use of path() rather than url() - if you give it a full url, it doesn't replace the http at the start, making it efectively useless.

Benubird
  • 18,551
  • 27
  • 90
  • 141
0

I came across this issue while trying to generate a route as form action in Blade using Laravel 5.4. Then I hit upon secure_url(), so I tried

{{ secure_url(route('routename', $args)) }} 

This still returned a non-secure URL. :-(

After digging through the code and adding some debug logs, I finally figured out that secure_url does not change the incoming url argument, if it's already an absolute URL (including the scheme).

Fortunately route has an absolute flag as the third argument, and returns a relative URL if $absolute is passed as false.

Assuming /a/{id}/b is a named route "a.b"

route('a.b', 1)        : will return http://[domain]/a/1/b 
route('a.b', 1, false) : will return /a/1/b

Joining the two I arrived at :

{{ secure_url(route('routename', $args, false)) }}

As expected it generated https://[domain]/routeXXX

:-)

79man
  • 31
  • 5
0

I had a problem with redirect trailing slashes after 2 hours of looking for a bug, just need to remove

.htaccess

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    RewriteEngine On

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)/$ /$1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

to

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    RewriteEngine On

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>
GetoX
  • 4,225
  • 2
  • 33
  • 30
-2

Place this in your filters.php file and everywhere will be forced to https while retaining URL parameters:

//force ssl
App::before(function () {
if(!Request::secure() && App::environment() != 'local')
{
    $baseHost = Request::getHttpHost();
    $requestUri = Request::getRequestUri();
    $newLink = 'https://'.$baseHost.$requestUri;

    return Redirect::to($newLink);
}});
  • This does not solve my problem. What happens to POST requests? If all of my routes are generated without https, then every link in the page will lead to a redirect. – Mārtiņš Briedis Nov 09 '16 at 21:27
  • Yeah I've never ran into that issue. When I generate my routes I typically use: – Anthony Farr Nov 12 '16 at 01:51
  • '"ajax": { "url": "{{ URL::route('getDepartmentTickets') }}", "type": "POST" },' To generate my routes and they already include https but I will look around my config other files to make sure I didn't do something else. Or maybe because the current page already has https so Laravel automatically just uses that. I'll play around with it and get back to you. – Anthony Farr Nov 12 '16 at 01:55
  • Also if you really are still having issues you could modify inside the IF statement to take all of the POST data and put it in a Session for the one request. They have a function Session::flash('key', 'value') that probably would work. https://laravel.com/docs/4.2/session#flash-data – Anthony Farr Nov 12 '16 at 02:01
  • Yeah Judging by your post up top it must be because my very first page(the landing page) of the application redirects and requires https before even logging into the system. Then from there out all of my URL routes are generated with https automatically? If this is true and you have a valid SSL cert I would just try to make the whole application https for simplicity's sake. – Anthony Farr Nov 12 '16 at 02:19
-2

According to the laravel documentation on the url() helper method.

If no path is provided, a Illuminate\Routing\UrlGenerator instance is returned

So you can use the secure method of the UrlGenerator class in the following way:

echo url()->secure('my_route_name');
-5

To generate a secure (https) route use the following built-in 'before' filter called 'auth':

For example:

Route::get('your-route', ['before' => 'auth', 'uses' => YourController@yourAction']);

Now when you output your link it will be prepended with 'https'

omar j
  • 521
  • 6
  • 8