Notatnik techniczny

.htaccess - Co powinien zawierać?

Szablon pliku .htaccess

Zamieszczony poniżej szablon nie zawiera adresu, pod jakim jest umieszczony. Można go zatem łatwo kopiować między projektami.

W związku z uwagami czytelników, jestem winien pewne wyjaśnienie. Określenie “jak powinien wyglądać plik .htaccess” jest tu skrótem myślowym. Podany poniżej szablon należy traktować jako przykład pliku .htaccess dla statycznego bloga, który może być inspiracją do dalszych modyfikacji celem spełnienia specyficznych wymagań zależnych od konkretnego przypadku.


<IfModule mod_mime.c>
    AddType application/vnd.ms-fontobject .eot
    AddType application/x-font-ttf .ttf
    AddType application/x-font-opentype .otf
    AddType application/x-font-woff .woff
    AddType application/font-woff2 .woff2
    AddType image/svg+xml .svg
</Ifmodule>


<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
    AddOutputFilterByType DEFLATE application/javascript application/x-javascript
    AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
    AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
    AddOutputFilterByType DEFLATE image/x-icon \
                                  image/svg+xml \
                                  application/vnd.ms-fontobject \
                                  application/x-font-ttf \
                                  font/opentype \
                                  application/x-font-woff \
                                  application/font-woff2
</Ifmodule>

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/html                         "access 5 minutes"
	ExpiresByType text/css                          "access plus 1 year"
    ExpiresByType image/jpg                         "access plus 1 year"
    ExpiresByType image/jpeg                        "access plus 1 year"
    ExpiresByType image/gif                         "access plus 1 year"
    ExpiresByType image/png                         "access plus 1 year"
    ExpiresByType image/svg+xml                     "access plus 1 year"
    ExpiresByType image/x-icon                      "access plus 1 year"
    ExpiresByType application/pdf                   "access plus 1 year"
    ExpiresByType application/x-javascript          "access plus 1 year"
    ExpiresByType application/javascript            "access plus 1 year"
    ExpiresByType application/vnd.ms-fontobject     "access plus 1 year"
    ExpiresByType application/x-font-ttf            "access plus 1 year"
    ExpiresByType application/x-font-opentype       "access plus 1 year"
    ExpiresByType application/x-font-woff           "access plus 1 year"
    ExpiresByType application/font-woff2            "access plus 1 year"
</IfModule>

<IfModule mod_headers.c>
    Header set Content-Security-Policy " \
        default-src 'self';\
        script-src 'self' 'unsafe-inline' https://www.google-analytics.com https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com https://www.googletagmanager.com *.disqus.com disqus.com *.disquscdn.com; \
        object-src 'none'; \
        frame-src https://disqus.com https://www.youtube.com; \
        img-src 'self' https://*.disqus.com https://*.disquscdn.com *.google-analytics.com https://cdn.viglink.com https://*.doubleclick.net; \
        style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://stackpath.bootstrapcdn.com https://cdn.jsdelivr.net https://c.disquscdn.com; \
        connect-src 'self' https://api.robertolechowski.com *.disqus.com; \
        font-src https://fonts.googleapis.com https://fonts.gstatic.com https://stackpath.bootstrapcdn.com https://cdn.jsdelivr.net; \
        base-uri 'self'"

    Header always set X-Content-Type-Options "nosniff"

    Header set Set-Cookie HttpOnly;Secure

    Header always set X-FRAME-OPTIONS "DENY"

    Header always set X-XSS-Protection "1; mode=block"

    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

    Header set Referrer-Policy "origin-when-cross-origin"

    Header set Feature-Policy: "microphone 'none'; \
                                camera 'none'; \
                                payment 'none'; \
                                geolocation 'self'"

</IfModule>

AddDefaultCharset UTF-8

RewriteEngine On

ErrorDocument 401 https://%{SERVER_NAME}/404
ErrorDocument 404 https://%{SERVER_NAME}/404
ErrorDocument 500 https://%{SERVER_NAME}/404

RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^ %{REQUEST_SCHEME}://%1%{REQUEST_URI} [R=301,L]

RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{SERVER_NAME}/$1 [R=301,L]

RewriteRule ^index\.html$ / [R=301,L]
RewriteRule ^(.*)/index\.html$ /$1/ [R=301,L]


ServerSignature Off

Jak to działa? – wyjaśnienie działania

Definiujemy typy zasobów

Definiujemy typy nowe zasobów na podstawie rozszerzenia pliku.

<IfModule mod_mime.c>
    AddType application/vnd.ms-fontobject .eot
    AddType application/x-font-ttf .ttf
    AddType application/x-font-opentype .otf
    AddType application/x-font-woff .woff
    AddType application/font-woff2 .woff2
    AddType image/svg+xml .svg
</Ifmodule>

Definiujemy kompresje

Ustalamy domyślny typ kompresji dla wybranych typów zasobów.

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
    AddOutputFilterByType DEFLATE application/javascript application/x-javascript
    AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
    AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
    AddOutputFilterByType DEFLATE image/x-icon \
                                  image/svg+xml \
                                  application/vnd.ms-fontobject \
                                  application/x-font-ttf \
                                  font/opentype \
                                  application/x-font-woff \
                                  application/font-woff2
</Ifmodule>

Konfiguracja czasu życia

Należy ustalić, jak długo zezwalamy przeglądarce przechowywać zasoby bez ich ponownego pobierania.

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/html                         "access 5 minutes"
	ExpiresByType text/css                          "access plus 1 year"
    ExpiresByType image/jpg                         "access plus 1 year"
    ExpiresByType image/jpeg                        "access plus 1 year"
    ExpiresByType image/gif                         "access plus 1 year"
    ExpiresByType image/png                         "access plus 1 year"
    ExpiresByType image/svg+xml                     "access plus 1 year"
    ExpiresByType image/x-icon                      "access plus 1 year"
    ExpiresByType application/pdf                   "access plus 1 year"
    ExpiresByType application/x-javascript          "access plus 1 year"
    ExpiresByType application/javascript            "access plus 1 year"
    ExpiresByType application/vnd.ms-fontobject     "access plus 1 year"
    ExpiresByType application/x-font-ttf            "access plus 1 year"
    ExpiresByType application/x-font-opentype       "access plus 1 year"
    ExpiresByType application/x-font-woff           "access plus 1 year"
    ExpiresByType application/font-woff2            "access plus 1 year"
</IfModule>

Ustawienie Content - Security - Policy

Dokunujemy zdefiniowania Content - Security - Policy. Czynimy to osobno dla różnych typów zasobów.

Header set Content-Security-Policy " \
    default-src 'self' https://*.disqus.com https://c.disquscdn.com\;
    script-src 'self' 'unsafe-inline' https://www.google-analytics.com https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com https://www.googletagmanager.com; \
    object-src 'none'; \
    frame-src https://disqus.com https://www.youtube.com; \
    img-src 'self'; \
    style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://stackpath.bootstrapcdn.com https://cdn.jsdelivr.net; \
    connect-src 'self' https://*.disqus.com; \
    font-src https://fonts.googleapis.com https://fonts.gstatic.com https://stackpath.bootstrapcdn.com https://cdn.jsdelivr.net; \
    base-uri 'self'"

Nagłówki związane z bezpieczeństwem

Przeglądarka nie będzie ‘zgadywać’ typu MIME zasobu na podstawie zawartości. (link)

Header always set X-Content-Type-Options "nosniff"

Zablokowanie dostępności ciasteczek z poziomu JavaScript. (link)

Header set Set-Cookie HttpOnly;Secure

Zablokowanie wyświetlania naszej strony w ramkach. (link)

Header always set X-FRAME-OPTIONS "DENY"

Utrudnienie potencjalnych ataków XSS (link)

Header always set X-XSS-Protection "1; mode=block"

Poinformowanie klienta, że prezentowana witryna powinna być dostępna wyłącznie przez https. (link)

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

Zdefiniowanie informacji, które będą przekazywane do obcych serwerów w nagłówku Referer. (link)

Header set Referrer-Policy "origin-when-cross-origin"

Wyłączenie wybranego API przeglądarki dla danej witryny (link)

Header set Feature-Policy: "vibrate 'none'; \
                            notifications 'none'; \
                            push 'none'; \
                            microphone 'none'; \
                            camera 'none'; \
                            payment 'none'; \
                            geolocation 'self'"

Przekierowania

Strony błędów

ErrorDocument 401 https://%{SERVER_NAME}/404
ErrorDocument 404 https://%{SERVER_NAME}/404
ErrorDocument 500 https://%{SERVER_NAME}/404

Przekierowanie do adresu bez www

RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^ %{REQUEST_SCHEME}://%1%{REQUEST_URI} [R=301,L]

Przekierowanie do protokołu https

RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{SERVER_NAME}/$1 [R=301,L]

Wyeliminowanie wyświetlania ‘.html’ na końcu nazwy danego zasobu

RewriteRule ^index\.html$ / [R=301,L]
RewriteRule ^(.*)/index\.html$ /$1/ [R=301,L]

Odnośniki