Using .htaccess to restrict access


Hi jawsmith,

thanks for your reply. You are right, first of all I should thank the PIWIK team, for their great piece of software.

Although I don’t know why you would like to block general access to “index.php”, as that would break access to the user interface of Piwik, …

Well, I actually don´t want to block it completely. My PIWIK is not yet protected by a “login attempts couter” (anti brute force protection), so I decided to protect the login area by .htpasswd .

Thanks for the provided link, but … you are wrong, if you say:

… my guess is that you are missing the concept of “regular expressions” in an Apache htaccess file.

As you probably know, the listening files can be bypassed through .htpasswd restriction with a directive like this:

<FilesMatch "^piwik.(js|php)$">
	Allow from all
	Satisfy any

(btw: I renamed these files on my PIWIK for your above mentioned reasons as well.)

But the OptOut text (that should appear eg. in the disclaimer of any of my websites) are taken from a index.php request with the appropriate query string (?module=CoreAdminHome&action=optOut). Bypassing these calls through .htpasswd can neither be managed by a “Files” or “FilesMatch” directive nor by “RewriteRule”, as far as I know. I could do something like vipsoft already mentioned in this thread (,17251,page=1#msg-17301) by adding the index.php to the “FilesMatch” part and involving my IP adress to another directive, but I dont´t have a static one, so this doesn´t make any sense.

You may now understand what´s my concern and what I meant with “improper programming” - anyway, this may sounds much too hard - I should better say: when implementing the OptOut feature, programmer was a little thoughtless … :wink:

Or can you or anyone else provide a suitable solution for that problem? I´d really appreciate.

Cheers, John


Isn´t there anybody who has the same or a similar problem?

I´d really like to find a solution for that … every suggestion is appreciated.

Thx, John


anyone got it to work with optout and dynamic ip yet?

best solution i found so far:

RewriteCond %{REQUEST_URI} ^/piwik/index.php$
RewriteCond %{QUERY_STRING} !action=optOut
RewriteRule ^(index).php$ http://my.domain/piwik/ [R=301,L]

<FilesMatch “index.php”>
Allow from all
Satisfy any

but the send email report is not working. :frowning:

and if i activate the HttpAuthLogin plugin the index.php gets endless 301.


My simple solution:

Options FollowSymLinks SymLinksIfOwnerMatch

<FilesMatch "(?<!^piwik\.js|^piwik\.php)$">
  AuthType Basic
  AuthName "Have a nice day!"
  AuthUserFile /absolute_path_to/.htpasswd
  AuthGroupFile /dev/null
  Require valid-user

  Order Allow,Deny
  Allow from my_ip_or_domain

  Satisfy all


This thread originally started quite a while ago… :wink:

To what extent are any of the suggested Apache configs still valid for the current version of Piwik (2.0.3 at the time of writing)? I’m assuming that most of the previous advice is for Piwik 1.x. Have there been any significant changes to the locations of files or additional .htaccess files added to the package as now released?

It sounds as though we all want much the same things:
[li] to ensure that our Piwik installation is secure from unauthorised access to files that should not be accessible to the public
[/li][li] to add additional access restrictions to the admin area (eg, by IP address, by local authentication scheme) on top of the Piwik username/password
[/li][li] and also to ensure that the Opt-Out form is still accessible to the public at large.

I wonder… how much of a nightmare would it be for the Piwik developers to rearrange the Piwik files (for a future release) into a hierarchy similar to:

/public/tracker/ <the basic required tracker files (piwik.js, piwik.php)…>
/public/services/ <the opt-out form and anything else that might need to be accessible to the public in the future… The fact that this is accessed via index.php seems to make things complicated? >

Would something like that make it easier for Piwik administrators to set up our installations securely? Or would it indeed be un cauchemar véritable?

Looking at my installation, there are the following .htaccess files supplied:


Should all of the other folders be entirely denied for remote browser access (does the admin area only require access to index.php via the browser and just ‘include’ the other files, or do any of them need to be accessed ‘directly’)?


Is anybody still following this thread? :wink:

I have been struggling for the past couple of days to try to set up my Piwik site so that I can protect against unauthorised access (ie, disallowing accesses which are not from an authenticated website user and not from a local IP address) to the admin area (and any other files) while still allowing all public access to the Opt-Out URI.

I find mod_rewrite (and friends) sometimes quite difficult at the best of times and only seem to be making some partial progress, unfortunately…

The following seems to work to disallow access to all files in general (so far, so good):

<Files "*">
    Order   Deny,Allow
    Deny from all
    Allow from [allowed IP range]

    AuthType  [our local authentication method]
    Require valid-user

I seem to also need to include index.php in my allowed Files list (NB: My Piwik installation is directly in the DocumentRoot, not within a /piwik folder), otherwise public access will be prevented by my ‘global’ Files IP and authentication restriction above…

# ~ means use regexp matching
<Files ~ "^piwik\.(js|php)|^index\.php$|robots\.txt$">
    Order   Deny,Allow
    Allow from all
    # Satisfy Any needed to override the stricter global Files rules
    Satisfy Any

The following Location section then seems to successfully override the more restrictive rules which were applied earlier in the parse order, and allows public access to the Opt-Out URI (hurrah):

# Location is *always* parsed after Files, so may override previous rules:  
# ~ means use regexp matching
<Location ~ "^/index.php?module=CoreAdminHome&action=optOut">
    Order   Deny,Allow
    Allow from all

Unfortunately, I now seem to be so close, yet still so far away…

If I access, then I first get asked to authenticate (using our local authentication method).

However, if I access, then the less restrictive Files section seems to match, and so I get presented with the Piwik login page straight away, meaning that neither our local authentication restriction nor the IP address restriction take effect, unfortunately.

I would still like to prevent (non-OptOut) access to the index.php page by unauthorised users. Malicious access would admittedly require knowledge of the Piwik login details (or exploitation of any potential vulnerability), but I would still prefer to have as many layers of defence available as possible.

Does anybody know of any improvements that I can make to my Apache config to restrict access further (while still allowing access to the Opt-Out URI)?

I am wondering if I could maybe do something such as removing index.php from the ‘allowed’ Files section, and then creating a mod_rewrite ruleset for a ‘fake’ URI (eg, which would call ‘behind-the-scenes’ to the opt-out URI, without actually changing the outwardly-facing URI (if somehow possible), but I have had too much of mod_rewrite for today to try to look into this further just yet…!

(Matthieu Aubry) #47

If you setup HTTP Auth for Piwik, make sure to use this plugin:


Thanks, Matt, the LoginHttpAuth plugin looks potentially useful.

We use CoSign <> (sorry, embedded links don’t always seem to work for me) as our local authentication type.

CoSign requires the installation of an Apache module, some Apache config directives (which I don’t need to detail here), and, regarding the specific content (Files, Location, etc) that you require authentication for:

CosignProtected  On
AuthType  Cosign
Require valid-user

Requests to a protected URI are redirected to the CoSign login page, and the environment variable REMOTE_USER is set after successful login.

Would the LoginHttpAuth plugin work with CoSign as well? The description says that it works with Basic HTTP Authentication (“AuthType Basic” ). You can see from the above that the required Apache config for CoSign is similar, but I don’t know how either method actually works in detail behind the scenes…

I don’t mind so much that a Piwik admin might need to login twice (ie, first to CoSign, just to even access the admin area URI, and then with a Piwik login to actually login to Piwik) if I can’t integrate the two, but I still have the problem that I while I want to protect the admin area, I still need to allow all public access to the OptOpt URI (ie, that needs to bypass all authentication restrictions). I think I possibly need to go back to perusing the mod_rewrite documentation again… :frowning:

(Matthieu Aubry) #49

Btw I’ve implemented Referrer Spam blacklist in Piwik! please contribute any new spamming domain in the ticket : Implement a default Referrer spam blacklist · Issue #2268 · matomo-org/matomo · GitHub


Dows Piwik work with blogger?


Runnning “apachectl configtest” I got:
Syntax error on line 271 of /etc/apache2/apache2.conf:
order takes one argument, ‘allow,deny’, ‘deny,allow’, or 'mutual-failure’
Action ‘configtest’ failed.

After I removed the blank space from

Order deny, allow

it worked


<Files "*">
    Order deny,allow
    Deny from all
    Allow from 128.252.135.
    Allow from
    Allow from

<Files ~ "^piwik\.(js|php)|robots\.txt$">
    Allow from all
    Satisfy any


Sorry to bump an old post, but is there an equivalent to this for Nginx?

<Files ~ "^piwik\.(js|php)|robots\.txt$">
    Allow from all
    Satisfy any

I have my /analytics directory protected with HTTP basic authentication, but need to give access to piwik.php and piwik.js, otherwise users are being prompted to login on each page with the tracking tag.

(Matthieu Aubry) #53

@Imm maybe this project helps you: GitHub - matomo-org/piwik-nginx: Nginx configuration for running Piwik

(Enrique Velasco) #54

Hi guys,

Piwik need to be protected with HTTPS but I have a shared hosting and I can’t modify php.ini file.

Can I force HTTPS only Piwik directory and not all my web?

Can I make this in .htaccess?

Thank you for all,



Hello! I can’t get password-verification to working properly. For some reason the “password verification-box” appears over and over again, even the login-name and password is correct. My host is hostGator.

My .htaccess -file is in piwik’s folder and .htpasswd -file is where it’s supposed to be.

# This file's path:
# /home/user/public_html/piwik/.htaccess

# Restrict outside access
AuthUserFile /home/user/.htpasswds/public_html/piwik/.htpasswd
AuthGroupFile /dev/null
AuthName "Piwik"
AuthType Basic

<Files "*">
   require valid-user

<Files ~ "^piwik\.(js|php)|robots\.txt$">
    Allow from all
    Satisfy any

# Error 500 when enabled;
#php_flag session.auto_start off

# Error 500 when enabled;
#Options -All


If using .htaccess from this post, public access is granted only for piwik.php and piwik.js .

On our server there is an gzipped version too piwik.js.gz, which is delivered if client performes Gzip request.

So, how to modify the following command for public access to piwik.js.gz too?

# ... except piwik.php and piwik.js which do not require authentication
<Files ~ "^piwik\.(js|php)|robots\.txt$">
    Allow from all
    Satisfy any
    Require all granted

So access should be granted to:

This works for me without ability for ‘OptOut’:

<Files ~ "^piwik\.js|piwik\.js\.gz|piwik\.php|robots\.txt$">

How to activate OptOut too?

Thanks + regards, p.


If no HTTPS is possible, please see 301 Moved Permanently for a plugin to at least encrypt passwords when transmitted from the browser to Piwik on login.


For those using NGINX, I made a best guess conversion from apache mod_rewrite to NGINX DSL. Sorry I didn’t preserve all credits in the comments, I was primarly focused on making it work in my config.

    location / {

      # block sql injection
      if ($query_string ~* "(;|<|>|'|\"|\)|%0A|%0D|%22|%27|%3C|%3E|%00).*(/\*|union|select|insert|cast|set|declare|drop|update|md5|benchmark)"){ return 403; } 
      if ($query_string ~ "\.\./\.\."){ return 403; } 
      if ($query_string ~* "(localhost|loopback|127\.0\.0\.1)"){ return 403; } 
      if ($query_string ~* "(<|>|'|%0A|%0D|%27|%3C|%3E|%00)"){ return 403; }

      # If the request query string contains /proc/self/environ
      if ($query_string ~ "proc/self/environ"){ return 403; } 

      # Block out any script trying to base64_encode or base64_decode data within the URL
      if ($query_string ~ "base64_(en|de)code[^(]*\([^)]*\)"){ return 403; } 

      # Block out any script that includes a <script> tag in URL      
      if ($query_string ~* "(<|%3C)([^s]*s)+cript.*(>|%3E)"){ return 403; } 

      # Block out any script trying to set a PHP GLOBALS variable via URL
      if ($query_string ~ "GLOBALS(=|[|\%[0-9A-Z]{0,2})"){ return 403; } 

      # Block out any script trying to modify a _REQUEST variable via URL
      if ($query_string ~ "_REQUEST(=|[|\%[0-9A-Z]{0,2})"){ return 403; }

      # file injection protection
      set $fileinjectionmethod 0;
      set $fileinjection 0;
      if ($request_method ~ "GET"){ set $fileinjectionmethod 1; } 
      if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+"){ set $fileinjection 1; } 
      if ($query_string ~* "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+"){ set $fileinjection 1; }
      set $injection "${fileinjectionmethod}${fileinjection}";
      if ($injection = 11) { return 403; }

      # easter egg protection
      if ($query_string ~* "\=PHP[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"){ return 403; }

      ## SQLi first line of defense
      if ($query_string ~* "concat[^\(]*\("){ return 403; } 
      if ($query_string ~* "union([^s]*s)+elect"){ return 403; } 
      if ($query_string ~* "union([^a]*a)+ll([^s]*s)+elect"){ return 403; }

      set $allow 0;
      # allow post
      if ( $request_method ~ "POST" ) { set $allow 1; }
      # allow put
      if ( $request_method ~ "GET" ) { set $allow 1; }
      # send forbidden on all disallowed
      if ( $allow = 0 ){
          return 403;

      # sub-domain prevention
      if ($http_host !~ "^piwik\.viasat\.com$"){ 
        rewrite ^(.*)$$1 redirect;

(rahul) #69

<Files “*”>…

(Towab Muhammad Yusuf) #70

This post was flagged by the community and is temporarily hidden.