In htaccess, how to set a response header for all URLs of except one? - apache

I want to use this rule:
<IfModule mod_headers.c>
Header always set X-FRAME-OPTIONS "DENY"
</IfModule>
But only for the front pages of my website.
I.e. I have a backoffice : example.com/gestion for which I don't want the rule to apply and I want to have the rule applied only for example.com (so all URLs without gestion)
Any idea ?

Try something like this using an Apache <If> expression to match all URLs, except for any URL that starts /gestion or contains multiple path segments or contains dots (ie. actual files).
For example:
<If "%{REQUEST_URI} =~ m#^/(?!gestion)[\w-]*$#">
Header always set X-FRAME-OPTIONS "DENY"
</If>
This uses a negative lookahead to avoid matching any URL that starts /gestion.
I'm assuming that your "front page" URLs only consist of single path segments containing characters in the range [0-9a-zA-Z_-].
The <IfModule> wrapper is not required (unless this is optional and you are using the same config on multiple server's where mod_headers may not be enabled - unlikely).

Related

How to set status code for certain URLs with IF in htaccess

I have URLs like https://example.com/page/1234/ and https://example.com/page/9876/. For these URLs i want set certain status code via htaccess (Apache 2.4).
I try to do it with
<If "%{REQUEST_URI} =~ (1234|9876)\/$">
Header set Status "HTTP/1.1 410 Gone"
</If>
but i seem to have an error in the code, because i don't see the new status code as response in developer tools. How should it be done on the correct way?
PS: i can't use 'rewriteRule' - this kind of setting 410 triggers ErrorDocument, what i don't want. For these two URLs i only want to set the status code. For other URLs, which get 410 on the native way, ErrorDocument shold be triggered.
You may use this block with <If> expression that uses default ErrorDocument 410 for a specific URL pattern:
RewriteEngine On
<If "%{REQUEST_URI} =~ m#/(1234|9876)/?$#">
# only for above URL disable ErrorDocument
ErrorDocument 410 default
# set status=410
RewriteRule . - [L,G]
</If>
Are you wanting the "normal page response" (as generated by your application), but with a 410 HTTP status?
100% correct: usual page, but with response status 410 Gone
Triggering the 410 response in Apache will always serve the appropriate Apache ErrorDocument. What you could do is set the 410 ErrorDocument itself to the same URL and trigger the 410 response in the usual way. However, we need to be careful not to create a "rewrite-loop".
For example:
<If "%{REQUEST_URI} =~ m#^/page/(1234|9876)/$# && %{ENV:REDIRECT_STATUS} == ''">
# Capture the URL-path after the slash prefix
SetEnvIf Request_URI "^/(.+)" REL_URL_PATH=$1
# Dynamic ErrorDocument to the same as the requested URL
# The slash prefix in the directive is necessary to be seen as a local URL-path
ErrorDocument 410 /%{reqenv:REL_URL_PATH}
# Trigger 410 Gone
RewriteRule ^ - [G]
</If>
This requires Apache 2.4.13+ due to the expression syntax in the ErrorDocument directive.
The check against the REDIRECT_STATUS in the <If> expression is necessary to avoid a rewrite loop (500 response) when serving the error document itself.
The alternative is to set the 410 response code in your application itself, which would be my preference.
Aside: Setting the Status HTTP response header, which appears to be what you are trying to do in the question simply sets a Status HTTP response header, it does not change the HTTP response status itself. (The Status response header is a non-standard header used by CGI scripts to indicate to the webserver what response code should be set.)
UPDATE: in my tests if i add to htaccess only the rule Header set Status "HTTP/1.1 410 Gone", without any condition, it works like i expect: all URLs from this directory get the header 410 Gone but are still available (ErrorDocument isn't triggered).
Maybe your server is configured differently and is perhaps behind a proxy that sets the HTTP response? But as mentioned above, that simply sets a Status HTTP response header, it doesn't change the HTTP response code on the request. It doesn't "work" on my test server. (If used at all, the Status header doesn't normally contain the protocol, it would simply be Header set Status "410 Gone".)
So, if this does work for you then you just need to "correct" the syntax in your Apache expression.
For example:
<If "%{REQUEST_URI} =~ m#/(1234|9876)/$#">
Header set Status "HTTP/1.1 410 Gone"
</If>
The above matches any URL that simply ends with /1234/ or /9876/. Or, to be more specific and match the entire URL use m#^/page/(1234|9876)/$#. This uses the alternative syntax for delimiting regex (ie. m#<regex>#), instead of using slashes (ie. /<regex>/), in order to avoid having to escape the slashes in the pattern.
Alternatively, you don't need the Apache expression, you could use SetEnvIf and set the header conditionally based on the environment variable.
For example:
SetEnvIf Request_URI "/(1234|9876)/$" GONE=1
Header set Status "HTTP/1.1 410 Gone" env=GONE
With your shown samples please try following. Please do clear your browser cache before testing your URLs.
RewriteEngine ON
RewriteRule ^page/(1234|9876)/?$ - [NC,R=410,L]

Why the files directive doesn't work in Apache's httpd.conf?

I had to noindex pdf files. I did it many times, so in this case, I used a files directive for adding noindex header with X-Robots-Tag, like Google recommends:
<Files ~ "\.pdf$">
Header set X-Robots-Tag "noindex, nofollow"
</Files>
When I have used this before, it worked like a charm. But in this case, I realized no X-Robots-Tag on its own, neither its content (noindex, nofollow) in header. Mod_headers was enabled.
I tried
<FilesMatch ~ "\.pdf$">
Header set X-Robots-Tag "noindex, nofollow"
</FilesMatch>
with no luck.
After many further tries and errors I've got it working with
<LocationMatch ~ "\.pdf$">
Header set X-Robots-Tag "noindex, nofollow"
</LocationMatch>
But I don't really understand why the rule I used for years stopped working and the rule I blindly tried, suddenly works.
Could somebody explain it to me?
The documentation for Apache states that FilesMatch takes a regular expression pattern <FilesMatch regexp> and is preferred over using <Files ~ "regexp">
The <FilesMatch> directive limits the scope of the enclosed directives by filename, just as the <Files> directive does. However, it accepts a regular expression.
In my experience with RegEx, this means using a wildcard to match all, rather than the normal <Files> directive which matches on a substring.
As for matching all named files in an expression, that means a small tweak is required to your existing code:
<FilesMatch ".+\.pdf$">
Header set X-Robots-Tag "noindex, nofollow"
</FilesMatch>
If you expect to have a file named .pdf that you also need to exclude, replace + in that expression with *. This is due to how RegEx matches:
. Match any character, once.
+ The previous modifier or block must occur one or more times
* The previous modifier or block may occur zero or more times
This means .+ matches all files with at least one character before .pdf in the filename, and .* matches all files ending on .pdf.
As for an explanation on why your Files directive doesn't work:
The Files directive may be overridden by other Files directives appearing later in the same configuration or within a .htaccess file in the directory you're keeping the pdf files in. Furthermore, there's an order in which the directives are handled and they can all override previous steps:
Directory < Files in Directory < .htaccess < Files in .htaccess < Location. So it's most probably a different part of the configuration that ignores the Files directive

htaccess: Access-Control-Allow-Origin wildcard for TLD? (.com, .org...)

Is there a wildcard for any top level domain in .htaccess for Access-Control-Allow-Origin?
I want to allow all top level domains (and any subdomain) of *.example.*.
So: example.com, example.org, any.example.com ...
Currently it works with:
SetEnvIf Origin ^(https?://.+\.example\.com(?::\d{1,5})?|https?://.+\.example\.org(?::\d{1,5})?)$ CORS_ALLOW_ORIGIN=$1
Header append Access-Control-Allow-Origin %{CORS_ALLOW_ORIGIN}e env=CORS_ALLOW_ORIGIN
Header merge Vary "Origin"
You can't specify a partial wildcard in the response.
You need to write code which examines the Origin request header, checks that it is acceptable and then echos it back in the response.

Set x-frame-options to allow and disallow certain URLs to frame a page

I want to prevent my website from being clickjacked by someone else. So in my .htaccess file, I placed this code (which is working correctly):
Header set X-Frame-Options SAMEORIGIN
My website is now secured from being iframed by any sites in the internet. However, I recently developed a mobile application that fetches pages under the about-us hosted in my website (my website contains www.mywebsite/about-us/author, www.mywebsite/about-us/company) to display the same details on the app. So what I did was I added the lines on my .htaccess file:
SetEnvIf REQUEST_URI ^about-us/$ aboutus_page
Header set X-Frame-Options SAMEORIGIN env=!aboutus_page
I want the rest of my pages to be free from being iframed except all pages under mywebsite/about-us/ "any page"
At least in Apache 2.4 %{REQUEST_URI} won't work for the usual SPA kind of URI. Use %{THE_REQUEST} instead. Then SetEnvIf is not as flexible so I recommend to use simply the // sections. Just tested the below and works:
<If "! %{THE_REQUEST} =~ /.*about-us.*/">
Header set X-Frame-Options SAMEORIGIN
</If>
you can make a second htaccess file in folder "aboutus" where you allow xframe.
so it will override the outer htaccess file.
if you just want to allow xframes in company and auther you can put htaccess files there too.

mod_rewrite help to change Content-disposition based on URI

I have a directory of mp3 files want to have be able to serve them inline or giving the user an option to download based on the request URI.
/media/file1.mp3 -- in this case, I just want to serve the file and let the browser play it.
/media/download/file1.mp3 -- in this case, I want to make it easy for a user to download the file instead.
I have been able to accomplish this with mod_rewrite and php (using the header() and readfile() function) but I would rather do it all with mod_rewrite, mod_header etc if possible.
IfDefine will check variables set on start-up of Apache so that won't work. A valid config would be:
SetEnvIf Request_URI ^/media/download/ force-download
Header set Content-Disposition attachment env=force-download
Also changing the Content-Type is not necessary to force a download.
With mod_rewrite you can only change some specific header fields but to which the Content-Disposition header field doesn’t belong. You could only change the Content-Type header field:
RewriteRule ^media/[^/]+\.mp3$ - [L,T=audio/mpeg]
RewriteRule ^media/download/[^/]+$ - [L,T=application/octet-stream]
And if you want to use a mod_headers+mod_setenvif solution:
SetEnvIf Request_URI ^/media/download/ force-download
<IfDefine force-download>
Header set Content-Disposition attachment
Header set Content-Type application/octet-stream
</IfDefine>
If you want to base rule on parameters in URI, here is the logic / syntaxe (adapt RewriteCond) :
RewriteCond %{QUERY_STRING} ^dl=1$
RewriteRule ^ - [L,E=FORCEDOWNLOAD:1]
Header set Content-Disposition attachment env=FORCEDOWNLOAD
Here, we force download if the only parameter is "dl=1"