URL Uppercase and Lowercase
Managing Uppercase in a URL

- Author:
-
Carlos Sánchez
- Topics:
- Crawling ,
- servers
- Publication Date:
- 2026-01-07
- Last Review:
- 2026-03-02
A URL can be distinguished from another simply by uppercase letters. This is known as Case Sensitive. By default, even if they are different URLs, certain technologies, such as the WordPress CMS, generate the same content.
This creates a page duplication as if it were a different version, and sometimes it has a self-canonical and a 200 response code. That is, it can lead to cannibalization and even be an entry point for negative SEO attacks.
That is, if you have the URL:
https://example.com/content/
It is a different URL from:
https://example.com/Content/
Depending on the CMS or framework, both may provide exactly the same content.
Hopefully, the canonical may point to the lowercase version. But depending on what is used for the canonical, it is not always guaranteed.
To avoid these issues, one can resort to the canonical, and many SEO extensions/plugins do this.
The 3 most accepted options for this practice are:
- Generate a 404 response code since these URLs do not exist and the duplicate makes no sense.
- Generate a canonical to the lowercase version to avoid cannibalization.
- Generate a redirect to the lowercase version, thus avoiding duplicate content, and it can help improve User Experience.
Redirect from Uppercase to Lowercase
This change must be made on the server. Since there are programming languages that allow greater flexibility, I do a combination between the server and the language being used. In this case, I combine Apache with PHP.
.htaccess
<IfModule mod_rewrite.c>
#Redirect from uppercase to lowercase
RewriteEngine On
RewriteBase /
# Look for any uppercase letters in the URL
RewriteCond %{REQUEST_URI} [A-Z]
# Avoid redirecting media content that could cause redirects to image files
RewriteCond %{REQUEST_URI} !.(css|js|woff|woff2|svg|jpg|jpeg|webp|avif|mp4|pdf|png|gif|bmp)$ [NC]
# Call a .php file placed in the root or the same folder as .htaccess where we will put the code below
RewriteRule ^(.*)$ lowercase.php?q=$1 [L,QSA]
</IfModule>
nginx
location / {
if ($request_uri ~* "[A-Z]") {
rewrite ^(.*)$ /lowercase.php?q=$1 last;
}
if ($request_uri ~* ".(css|js|woff|woff2|svg|jpg|jpeg|webp|avif|mp4|pdf|png|gif|bmp)$") {
break;
}
}
PHP File
It must have the same name as the file being called, as follows.
<?php
// Redirect the uppercase version to the lowercase version. Called from .htaccess
if(isset($_GET['q'])) {
$url = strtolower($_GET['q']);
$request_uri = $_SERVER['REQUEST_URI'];
// Here we make a conditional so that it always has a trailing slash, avoiding duplication. Otherwise, it should be reversed: remove the slash if it exists
if($url !== $_GET['q'] || substr($request_uri, -1) !== '/') {
if(substr($request_uri, -1) === '/') {
$new_url = str_replace($_GET['q'], $url, $request_uri);
} else {
$new_url = str_replace($_GET['q'], $url, $request_uri) . '/';
}
header("HTTP/1.1 301 Moved Permanently");
header("Location: ".$new_url);
exit();
}
}
?>
Extra: Hashbangs
URL fragments cannot be redirected from the server since it is an interaction between the browser and the user. The only way to redirect these URLs is through JavaScript, although this is usually not a problem since, with few exceptions, Google does not crawl these parameters. Therefore, this implementation would simply be to improve user experience.
But it can be done using JavaScript. Here is an example of how to redirect all fragments to their lowercase version, replacing "_" with "-". This code also ensures that the same scrolling behavior is maintained as if the redirect were applied correctly.
document.addEventListener('DOMContentLoaded', function() {
var fragment = decodeURI(window.location.hash.substr(1));
if (fragment.indexOf('') !== -1) {
var newFragment = fragment.replaceAll('', '-').toLowerCase();
history.replaceState(null, '', window.location.href.replace(fragment, newFragment));
} else if (fragment.match(/[A-Z]/)) {
var newFragment = fragment.toLowerCase();
history.replaceState(null, '', window.location.href.replace(fragment, newFragment));
}
// If there is a URL fragment, find the corresponding element and scroll to it
if (fragment) {
var targetElement = document.getElementById(fragment);
if (targetElement) {
setTimeout(function() {
targetElement.scrollIntoView();
}, 100); // Wait 100ms before scrolling
}
}
});
Code explanation.
For this code to work correctly, it should perform the scrolling effect to the corresponding ID. Normally, the browser does this automatically. But if we implement this type of redirection, we need to provide a little help.
To make the browser scroll to the corresponding section, you must use the scrollIntoView() property on the element that has the ID corresponding to the URL fragment.
In this code, after changing underscores and uppercase letters, it checks if there is a URL fragment. If so, it searches for the corresponding element in the DOM using document.getElementById(), and if found, it scrolls to it using scrollIntoView(). This ensures the page scrolls to the correct section when loaded with a URL fragment in the address bar.
In this case, to force the scroll, it is useful to apply a brief delay before calling scrollIntoView() to allow the browser to finish rendering the page.
Apache code explanation
- <IfModule mod_rewrite.c>: Starts a conditional section that checks if the mod_rewrite module is enabled on the Apache server. This ensures the rules block runs only if the module is active.
- RewriteEngine On: Activates the rewrite engine, allowing rewrite rules in the .htaccess file to be processed.
- RewriteBase /: Sets the base for rewrite rules. In this case, it is set to the website root.
- RewriteCond %{REQUEST_URI} [A-Z]: This condition checks if the URL contains at least one uppercase letter in the path.
- RewriteCond %{REQUEST_URI} !.(css|js|woff|woff2|svg|jpg|jpeg|webp|avif|mp4|pdf|png|gif|bmp)$ [NC]: This condition excludes redirection for URLs ending with specific file extensions (media content), thus avoiding unnecessary redirection of those resources. The [NC] modifier makes the comparison case-insensitive.
- RewriteRule ^(.*)$ lowercase.php?q=$1 [L,QSA]: This rule performs the redirection. It takes the requested URL and redirects it to lowercase.php, passing the original URL as the parameter q. The [L] modifier indicates this is the last rule to execute, and [QSA] appends existing query string variables to the new URL.
- </IfModule>: Closes the conditional mod_rewrite module section.
Nginx code explanation
- location /: Defines how the root location will be handled.
- if ($request_uri ~* "[A-Z]"): Checks if the request contains uppercase letters in the URL.
- rewrite ^(.*)$ /lowercase.php?q=$1 last;: If the request contains uppercase letters, this rewrite line redirects the request to lowercase.php, passing the original URL as the q parameter. The last modifier indicates this is the final instruction in this location block.
- if ($request_uri ~* ".(css|js|woff|woff2|svg|jpg|jpeg|webp|avif|mp4|pdf|png|gif|bmp)$"): Checks if the request ends with one of the listed media file extensions. If so, the request is stopped and the rewrite is not applied.
Note that excessive use of if statements in Nginx is not recommended due to its impact on performance.
References
I currently offer advanced SEO training in Spanish. Would you like me to create an English version? Let me know!
Tell me you're interested