2

I am working with a URL shortener (YOURLS) and I'm trying to have a certain page not directed to the link processing script. Here's the original .htaccess:

# BEGIN YOURLS
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
# THE LINE I'M ADDING
# RewriteCond %{REQUEST_URI} !^/InvalidLink\.html$
RewriteRule ^.*$ /yourls-loader.php [L]
</IfModule>
# END YOURLS
RewriteCond %{HTTP_HOST} ^www\.example\.com$
RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^(.*)$ "http\:\/\/example\.com\/$1" [R=301,L]

I've added a new condition right before the RewriteRule line (commented out and pointed out above):

RewriteCond %{REQUEST_URI} !^/InvalidLink\.html$

to have http://example.com/InvalidLink.html ignored. But this is making no difference. What am I doing wrong here?

  • That's the correct condition, however, where are you putting it? – MrWhite Feb 8 '17 at 17:24
  • 1
    Right before RewriteRule line – lfk Feb 8 '17 at 18:18
  • That already sounds correct, so there would seem to be something else going on here? Have you cleared your browser cache? If YOURLS uses 301 redirects then these will have been cached by your browser. Does /InvalidLink.html exist as a physical file? – MrWhite Feb 9 '17 at 8:48
  • I have cleared the cache. But InvalidLinks.html does not exist. Let me try that – lfk Feb 9 '17 at 10:39
  • The only thing with if InvalidLinks.html actually existed as an actual file (since it just looks like a real file) is that the existing directives would already exclude it from being rewritten. – MrWhite Feb 9 '17 at 12:50
1
# THE LINE I'M ADDING
# RewriteCond %{REQUEST_URI} !^/InvalidLink\.html$

There is nothing "wrong" with the condition you are adding, providing the requested URL is exactly /InvalidLink.html (case-sensitive match), this .htaccess file is in the document root and the browser cache has been cleared before testing. So, this is still a puzzle. Try adding the NC flag - in case there is a mismatch in case, for example:

RewriteCond %{REQUEST_URI} !^/InvalidLink\.html$ [NC]

The fact that the exception "works" if you first create this file, in which case the first condition (!-f) prevents the rewrite happening, suggests that the file is being processed OK and your cache is clear. And that the above condition is at fault. However, from what you have posted it looks OK!?

An alternative to adding a condition to your existing rule, is to create a separate "exception" at the top of your file. So, instead of saying, "rewrite the URL if the request is not for /InvalidLink.html", we can add a separate rule that states "if the URL is /InvalidLink.html then stop here and do nothing more".

So, instead of your condition, include a separate rule at the top of your file:

RewriteRule ^InvalidLink\.html$ - [NC,L]

Note there is no slash prefix on the RewriteRule pattern in per-directory .htaccess files. This will match /InvalidLink.html and prevent the following mod_rewrite directives from being processed. Ideally, the NC flag should not be necessary, however, I've included it "just in case".


This is really subsidiary to your main problem above...

RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$

This carbuncle is what cPanel automatically adds before every RewriteRule when implementing the "Let's Encrypt" SSL cert. It's very messy and there is much argument in the cPanel forums requesting this be removed or changed. (Why they can't add a single code block/exception at the top of the file is a mystery.)

Anyway, in the interim, these directives can be safely removed (throughout the file) - they are not required (after the cert has been installed). This will simplify your directives, making the file more readable (and avoid any potential conflict). However, cPanel will later automatically re-add these directives when the certs are renewed (every 3 months).

RewriteCond %{HTTP_HOST} ^www\.example\.com$
RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^(.*)$ "http\:\/\/example\.com\/$1" [R=301,L]

As mentioned above, the middle conditions can be safely removed. However, this canonical redirect is in the wrong place! It needs to be at the very top of your file, otherwise none of your shortened URLs will be canonicalised. In short, cPanel is not very good (unreliable) at editing the .htaccess file!

Summary

Bringing all the above together, your existing .htaccess file can be rewritten as:

RewriteEngine On

# Canonical www to non-www redirect
RewriteCond %{HTTP_HOST} ^www\.(.+) [NC]
RewriteRule (.*) http://%1/$1 [R=301,L]

# Exceptions - avoid these URLs being rewritten
RewriteRule ^InvalidLink\.html$ - [NC,L]

# BEGIN YOURLS
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* /yourls-loader.php [L]
# END YOURLS

The <IfModule> wrapper is not required. And the RewriteBase directive was superfluous. .* is the same as ^.*$. Backslash escaping chars in the RewriteRule substitution is unnecessary (that's "typical cPanel").

  • You're right. www.example.com/keyword fails to redirect. YOURLS is expecting naked, but it gets www. – lfk Feb 11 '17 at 4:28
  • In the first RewriteRule, how does %1 match domain name? e.g., what if we had two conditions, each with its own groups? – lfk Feb 11 '17 at 5:02
  • 1
    Okay. I finally figured it out. What was happening was that, since InvalidLink.html did not exist, the server was redirecting to the 404 error page (not sure where it's defined), which also did not exist, and thus would be forwarded to YOURLS! I added an ErrorDocument directive and created a 404 page. Now InvalidLink.html shows the 404 page. Though in practice, a 404 will never happen. Everything not a file will go to YOURLS, which does a 302 to another error page. – lfk Feb 11 '17 at 7:56
  • %1 is a backreference to the first captured group in the last matched CondPattern. So, in the above example, %1 is a backreference to the (.+) subpattern in the ^www\.(.+) CondPattern (if it matched). If you had two conditions then the backreference refers to the last one that matched (which isn't necessarily the last RewriteCond directive). – MrWhite Feb 11 '17 at 18:42
  • "...and thus would be forwarded to YOURLS!" - That really shouldn't have happened. It suggests that the ErrorDocument defined in the server config was misconfigured. A missing error document by itself wouldn't normally have caused this problem. But if the ErrorDocument specified an absolute URL (instead of a root-relative URL) then it would have triggered an external redirect which would have resulted in the YOURLS script from answering the second (redirected) request. ErrorDocument 404 default would also resolve it. Bit of a guess but that seems to fit. Glad you sorted it! :) – MrWhite Feb 11 '17 at 19:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.