Apache - Add Cache-Control header if not already added - apache

In Apache, how can I set a Response Header only if it is not already set by the CGI application?
I need a way to automatically add the Cache-Control header to static content on my website, but I want the CGI application to be able to specify its own Cache-Control header too.
SetEnvIf will not work for this purpose because it only matches Request Headers.
Is there some way to conditionally / optionally set a header if it is not already set?

1) Configure Apache to append the value of empty string ("") to the Cache-Control header to ensure the header is always included in the response.
2) Configure Apache to set the Cache-Control header only if it's still set to empty string.
<FilesMatch "\.(css|ico|flv|gif|jpeg|jpg|js|pdf|png|swf)$">
Header append Cache-Control ""
Header edit Cache-Control "^[, ]*$" "max-age=1800, public"
</FilesMatch>

Related

Adding multiple domain/URL in Header set X-Frame-Options "ALLOW-FROM "

I want to add multiple domains/URL in Header set X-Frame-Options "ALLOW-FROM " (eg: Header set X-Frame-Options "ALLOW-FROM http://xyz, http://abc") in httpd.conf file . But I am unable to add 2 domains/URL as specified above. My requirement is the the X-Frame contents should be displayed only if the page is accessed from 2 different domains. So obviously I cannot give SAMEORGIN. Please specify any alternate solutions to add 2 domains/URL in X-Frame-Options.
Add this to your apache configuration:
<IfModule mod_headers.c>
Header set Content-Security-Policy "frame-ancestors http://*.example.com/ 'self';"
# For IE 11 and below
Header set X-Frame-Options SAMEORIGIN
Header append X-Frame-Options "ALLOW-FROM http://example.com/" </IfModule>

Apache mod_header strange behavior

############################
# FILES - CACHING: CONTROL #
############################
Header set Cache-Control "max-age=2592000, public"
<FilesMatch "\.(?:bmp|css|cur|gif|ico|jp(?:eg?|g)|js|png|svgz?|tiff?|webp)$">
Header set Cache-Control "max-age=31536000, public"
</FilesMatch>
<FilesMatch "\.(?:html?|json|php|xml)$">
Header set Cache-Control "max-age=0, must-revalidate, no-cache, no-store, post-check=0, pre-check=0, private"
</FilesMatch>
<FilesMatch "\.(?:atom|rdf|rss)$">
Header set Cache-Control "max-age=3600, public"
</FilesMatch>
Header merge Cache-Control "no-transform"
This is a snippet of my htacces in which I handle the Cache-Control header. By default, for all the files, it is set to max-age=2592000, public.
On the bottom I'm always trying to append a no-transform directive but it simply doesn't work. It... well it just does nothing. The no-transform directive is never been set. I tried replacing merge with append, nothing changes.
Now, this is what I noticed. If I replace the third FilesMatch directive with:
<FilesMatch "\.(?:atom|rdf|rss)$">
Header set Cache-Control "max-age=3600, public"
Header merge Cache-Control "no-transform"
</FilesMatch>
It works. Every feed file of my site will have the no-transform directive. If I change the last line of my first snippet to:
<FilesMatch "^.+$">
Header merge Cache-Control "no-transform"
</FilesMatch>
Well... it works for all the files. Just... WHY?!
I also noticed another very strange behavior concerning headers. Let's say I want to force a keep-alive on connections. I insert this somewhere inside my htaccess file:
Header merge Connection "Keep-Alive"
And this is what I get in my response header:
Connection: Keep-Alive, Keep-Alive
Again... WHY?!
I suspect (though can't find any documentation to back this up as it's not 100% clear from this page: https://httpd.apache.org/docs/2.4/sections.html), that FilesMatch directives are processed after directives which are not in FileMatch.
Therefore even though you write this:
Header set Cache-Control "max-age=2592000, public"
<FilesMatch "\.(?:atom|rdf|rss)$">
Header set Cache-Control "max-age=3600, public"
</FilesMatch>
Header merge Cache-Control "no-transform"
Apache processes it like this:
Header set Cache-Control "max-age=2592000, public"
Header merge Cache-Control "no-transform"
<FilesMatch "\.(?:atom|rdf|rss)$">
Header set Cache-Control "max-age=3600, public"
</FilesMatch>
Which means the "later" set overrides the earlier merge. If you change the "set" in your FilesMatch pieces to "merge" you should see it work as it will no longer override it.
Though to be honest I think this way of writing you config is very difficult to follow. Since you are setting you cache control header four times why not just set the no-transform at the same time in each of the four set directives? Yes it's a tiny bit of repetition but a lot clearer and means you won't run into these order problems!
Your second issue is easier to explain from the documentation: http://httpd.apache.org/docs/current/mod/mod_headers.html#header:
merge ... Values in double quotes are considered different from otherwise identical unquoted values
So if value is Keep-Alive and you add "Keep-Alive" then you end up with "Keep-Alive Keep-Alive".
Btw as an aside you should not try to enable Keep-Alives just by setting the header. While that will work for the client, Apache needs to also keep the connection alive for the client to connect to, which it won't just by setting this header. So the client will think the connection is being kept alive but in fact it won't be as the server will close the connection anyway. You need to set it with the config like the following (which will also set the necessary headers for you):
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5

Apache httpd: Conditionally set response header based on other *response* header

Is there a way to set a new response header conditionally, where the condition uses another response header? Specifically, the new response header should be set only if the response has a certain Content-Type.
I have looked into mod_headers in combination with mod_setenvif but it looks like conditions can only use request headers, not response headers.
Thanks, John
Apache 2.4 is the answer:
Set Cache-Control header when response content type is application/pdf
Header set Cache-Control "no-store,no-transform" "expr=%{resp:Content-Type} =~ m|application/pdf|"
Do NOT try with the IF directive. It is evaluated too early in the process. For example, the following will NOT work:
<If "%{resp:Content-Type} =~ m|application/pdf|">
Header set Cache-Control "no-store, no-transform"
</If>

handle multiple domains with Access-Control-Allow-Origin header in Apache

I want to configure apache for cross-domain access header. I have tried multiple combination as suggested on number of threads on the forum. But its not working for me.
The ways, I have tried:
1) Specify domain on different line as below with Header set :
Header set Access-Control-Allow-Origin "example1.com"
Header set Access-Control-Allow-Origin "example2.com"
Header set Access-Control-Allow-Origin: "example3.com"
With this setup its picking only last one and ignore rest of all.
2) Specify domain on different line as below with Header add :
Header add Access-Control-Allow-Origin "example1.com"
Header add Access-Control-Allow-Origin "example2.com"
Header add Access-Control-Allow-Origin: "example3.com"
With this its showing all three domains in header, but fonts are not getting picked up on Firefox.
3.) Tried Using SetEnvIf, but again its not working :
SetEnvIf Origin "http(s)?://(www\.)?(mydomain.com|mydomain2.com)$" AccessControlAllowOrigin=$0$1
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Finally working with "*", but I don't want to use this.
Please help with this.
For 3 domains, in your .htaccess:
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(domain1.org|domain2.com|domain3.net)$" AccessControlAllowOrigin=$0$1
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true
</IfModule>
I've tried this and it works for me. Let me know if it doesn't for you.
Unless I'm misunderstanding the manual, it should be:
Header always append Access-Control-Allow-Origin: "example1.com"
Header always append Access-Control-Allow-Origin: "example2.com"
Header always append Access-Control-Allow-Origin: "example3.com"
The manual states that the set and add actions behave in the following way:
set: "The response header is set, replacing any previous header with this name"
add: "...This can result in two (or more) headers having the same name. This can lead to unforeseen consequences..."
To restrict access to certain URIs checkout these docs:
CrossOriginRequestSecurity
Server-Side Access Control#Apache_examples
One helpful trick is to use an Apache rewrite, environment variable, and headers to apply Access-Control-Allow-* to certain URIs. This is useful, for example, to constrain cross-origin requests to GET /api(.*).json requests without credentials:
RewriteRule ^/api(.*)\.json$ /api$1.json [CORS=True]
Header set Access-Control-Allow-Origin "*" env=CORS
Header set Access-Control-Allow-Methods "GET" env=CORS
Header set Access-Control-Allow-Credentials "false" env=CORS
Also, in general, according to W3 Wiki - CORS Enabled#For_Apache
To expose the header, you can add the following line inside Directory, Location, and Files sections, or within an .htaccess file.
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
AND, you can use add rather than set, but be aware that add can add the header multiple times, so it's generally safer to use set.
Try this one, it works for me.
Apply in .htaccess:
SetEnvIf Origin "^http(s)?://(.+\.)?(domain\.org|domain2\.com)$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
Will be work 100%, Apply in .htaccess:
# Enable cross domain access control
SetEnvIf Origin "^http(s)?://(.+\.)?(domain1\.com|domain2\.org|domain3\.net)$" REQUEST_ORIGIN=$0
Header always set Access-Control-Allow-Origin %{REQUEST_ORIGIN}e env=REQUEST_ORIGIN
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header always set Access-Control-Allow-Headers "x-test-header, Origin, X-Requested-With, Content-Type, Accept"
# Force to request 200 for options
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]
For Apache 2.4, I have used SET command for the Apache web server to set Header dynamically.
<IfModule mod_deflate.c>
# CORS
SetEnvIfNoCase Origin "http(s)?://(\w+\.)?(example.com|localhost)(:[0-9]+)?$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true
</IfModule>
ADD command didn't work for me.
I am using this in my .htaccess file for allowing access to multiple domains
<ifModule mod_headers.c>
SetEnvIf Origin "http(s)?://(localhost:25120|domain.com|domain2.com)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header always set Access-Control-Allow-Methods: "GET,POST,OPTIONS,DELETE,PUT"
</ifModule>
For Multiple domains, in your .htaccess:
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(domain.com|domain.online|domain.in|domain.net|domain.site|domain.website|domain.space)$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header set Access-Control-Allow-Credentials true
</IfModule>
it 100% works for me
This works for me in Classic ASP:
If Request.ServerVariables("HTTP_ORIGIN") = "http://domain1.com" Then
Response.AddHeader "Access-Control-Allow-Origin","http://domain1.com"
ElseIf Request.ServerVariables("HTTP_ORIGIN") = "http://domain2.com" Then
Response.AddHeader "Access-Control-Allow-Origin","http://domain2.com"
'and so on
End If

How to speed up web development with correct Apache caching headers setup?

Despite various measure ments to setup correct caching code in htaccess file, I still get this error:
Specify a cache validator:
All static resources should have either a Last-Modified or ETag header. This will allow browsers to take advantage of the full benefits of caching.
Is there anything wrong with my htaccess caching settings? If you have improvements for these settings i will be very happy to hear. Thank you very much for your suggestions.
<IfModule mod_headers.c>
Header unset Pragma
FileETag None
Header unset ETag
ExpiresActive On
##### DYNAMIC PAGES
<FilesMatch "\\.(ast|php|css)$">
Header set Cache-Control "public, max-age=3600, must-revalidate"
</FilesMatch>
##### STATIC FILES
<FilesMatch "\\.(png|svg|swf|js|xml)$">
Header set Cache-Control "public, max-age=604800, must-revalidate"
Header unset Last-Modified
</FilesMatch>
##### ETERNAL FILES
<FilesMatch "\\.(ico|jpg|gif|ttf|eot|pdf|flv)$">
Header set Cache-Control "public, max-age=7257600, must-revalidate"
Header unset Last-Modified
</FilesMatch>
</IfModule>
You are specifically unsetting the Last-Modified header. That's the cache validator section. Remove those lines that include:
Header unset Last-Modified
Also, is your css really dynamic? CSS can be huge for a lot of websites. Try to cache that just like any other static content.
All static resources should have either a Last-Modified or ETag header. This will allow browsers to take advantage of the full benefits of caching.