Skip to content

A site's root URL always has a trailing slash, regardless of addTrailingSlashesToUrls #5675

Closed

Description

@benface

Steps to reproduce

  1. Set the general config option addTrailingSlashesToUrls to false (or just don't set it, as it's the default)
  2. Write the following in a template: {{ siteUrl() }}
  3. Render that template from a site that has a base URL that ends with a path, like https://example.com/en

Expected result: https://example.com/en
Actual result: https://example.com/en/

Additional info

  • Craft version: 3.4.5
  • PHP version: 7.3.13
  • Database driver & version: MySQL 5.5.5

Activity

  1. added this to the 3.5 milestone on Feb 18, 2020
  2. dominikkrulak commented on Feb 25, 2020

    @dominikkrulak

    @benface This isn't Craft CMS's related solution but you could solve it with web server. I use NGINX and all trailing slashes at the end of URI that web server match rewrites and friendly 301 redirects it to URI with no trailing slash.

    server {
        rewrite /(.*)/$ /$1 permanent;
    }
    

    Your actual result: https://example.com/en/
    After using rewrite: https://example.com/en

    As this minor bug is coming from Craft CMS than it'd solved.

  3. benface commented on Feb 25, 2020

    @benface
    ContributorAuthor

    @dominikkrulak Thank you. Yes, I know and already do this. The issue is that following a link to the home page causes a 301 redirect (so two requests instead of one), since the original link from Craft contains a trailing slash, and the web server redirects to the no-trailing-slash URL.

  4. modified the milestones: 3.5, on Apr 1, 2020
  5. brandonkelly commented on Apr 1, 2020

    @brandonkelly
    Member

    Unfortunately this is going to have to wait until 4.0, because it would end up breaking a lot of templates that currently do things like:

    <link rel="stylesheet" type="text/css" href="{{ siteUrl }}assets/style.css">

    That assumes that siteUrl will always end in a slash.

  6. benface commented on Apr 1, 2020

    @benface
    ContributorAuthor

    Ah yeah, I see. No problem at all.

  7. brandonkelly commented on Apr 1, 2020

    @brandonkelly
    Member

    Also, while this is definitely a bit awkward for site URLs that include a URI segment (e.g. http://example.com/en), the current behavior makes sense for base URLs without one (e.g. http://example.com/) – as browsers will end up redirecting to http://example.com/ even if you go to http://example.com (no slash). That’s not immediately obvious by looking at the address bar, but try going to github.com and outputting document.location.href in your browser console – it will output https://github.com/.

  8. mizziness commented on Jun 8, 2020

    @mizziness

    If use SEOmatic and you're looking for a temporary work-around for this issue, this is how I solved it for our sites temporarily until the fix comes out:

    {% set currentUrl = currentSite.handle == 'english' ? entry.url : entry.url|trim('/', 'right') %}
    {% do seomatic.meta.canonicalUrl(currentUrl) %}

    For our sites, this gives us:

    It's definitely not hack-free, but it works nicely and it's only one extra line of code in my template.

  9. modified the milestones: , on May 3, 2021
  10. removed this from the milestone on Jun 3, 2022
  11. WHITE-developer commented on Jun 5, 2024

    @WHITE-developer

    Very old issue, but it's still there. Any update on this?

  12. brandonkelly commented on Jun 5, 2024

    @brandonkelly
    Member

    Sorry, looks like this fell through the cracks. Just added it to our v6 list. In the meantime, the previous workaround will still do the trick.

  13. tibbis commented on Dec 25, 2024

    @tibbis

    Noticed this issue on my end as well - what is the temporary solution for alternate and sitemaps urls (seomatic) etc? Affects single home page entry url with subdirectory like example.com/de.

  14. khalwat commented on Dec 28, 2024

    @khalwat
    Contributor

    While I think this is largely as non-issue, and will not affect SEO in any measurable way, I've implemented it in SEOmatic.

    Craft CMS 3:

    You can try it now by setting your semver in your composer.json to look like this:

        "nystudio107/craft-seomatic": "dev-develop as 3.5.10”,

    Then do a composer clear-cache && composer update

    …..

    Craft CMS 4:

    You can try it now by setting your semver in your composer.json to look like this:

        "nystudio107/craft-seomatic": "dev-develop-v4 as 4.1.9”,

    Then do a composer clear-cache && composer update

    …..

    Craft CMS 5:

    You can try it now by setting your semver in your composer.json to look like this:

        "nystudio107/craft-seomatic": "dev-develop-v5 as 5.1.9”,

    Then do a composer clear-cache && composer update

  15. Golembivskyi commented on Jul 8, 2025

    @Golembivskyi

    Is there any updates about fixing this bug?

  16. brandonkelly commented on Jul 8, 2025

    @brandonkelly
    Member

    Sorry, no. It would be a breaking change (e.g. for anyone doing {{ url() ~ "some-path" }}), so if we ever change it, it would need to be in a major release, but I also don’t consider it a real bug, because URLs always have some sort of URI, even if it’s just /.

    While there is a meaningful difference between https://abc.com/foo and https://abc.com/foo/ from SEO/browser caching perspectives, the same is not true for https://abc.com and https://abc.com/ – both are the exact same URL (the former will automatically normalize to the latter).

  17. benface commented on Jul 9, 2025

    @benface
    ContributorAuthor

    Interesting; I wouldn't expect site.baseUrl to change depending on this (isn't it static based on how it is defined in the project config?), but rather how URLs are resolved in functions such as url() and siteUrl(). But totally fair!

  18. brandonkelly commented on Jul 10, 2025

    @brandonkelly
    Member

    @benface Sorry yeah you’re right. Updated my example to use url().

  19. brandonkelly commented on Oct 1, 2026

    @brandonkelly
    Member

    This is now resolved for Craft 6 🎉 (#19723)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugseverity:minorCosmetic issues or bugs with simple workarounds

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions