{"id":208,"date":"2009-07-27T00:12:18","date_gmt":"2009-07-26T23:12:18","guid":{"rendered":"http:\/\/eriz.pcinside.pl\/weblog\/mod_rewrite-pozornie-beznadziejne-problemy-ktore-mozna-rozwiazac-208.html"},"modified":"2009-07-27T00:12:18","modified_gmt":"2009-07-26T23:12:18","slug":"mod_rewrite-pozornie-beznadziejne-problemy-ktore-mozna-rozwiazac","status":"publish","type":"post","link":"https:\/\/eriz.pcinside.pl\/weblog\/mod_rewrite-pozornie-beznadziejne-problemy-ktore-mozna-rozwiazac-208.html","title":{"rendered":"mod_rewrite &#8211; (pozornie) beznadziejne problemy, kt\u00f3re mo\u017cna rozwi\u0105za\u0107"},"content":{"rendered":"<p><em>mod_rewrite<\/em>, rewriting, <em>przyjazne URL-e<\/em>\/adresy, maskowanie, przepisywanie, nazw jest sporo. Zreszt\u0105, najpopularniejsza pochodzi od swojego protoplasty &#8211; czyli <em>mod_rewrite<\/em> powsta\u0142ego pod skrzyd\u0142ami Apache&#8217;a jako modu\u0142. Teraz w\u0142a\u015bciwie standard, je\u015bli chodzi o nowoczesne strony www &#8211; nie tylko ze wzgl\u0119du na wygl\u0105d, ale i (jak ptaszki \u0107wierkaj\u0105, cho\u0107 jest to w\u0105tpliwe wobec <a href=\"http:\/\/feedproxy.google.com\/~r\/blogspot\/amDG\/~3\/oWX6FvIxDuo\/dynamic-urls-vs-static-urls.html\">oficjalnych \u017ar\u00f3de\u0142<\/a>) SEO.<\/p>\n<p>Jak zwa\u0142, tak zwa\u0142, adres <em>http:\/\/example.org\/kawalek\/adresu<\/em> wygl\u0105da du\u017co estetyczniej i jest \u0142atwiejszy do zapami\u0119tania ni\u017c potworki typu <em>http:\/\/example.org\/?kawalek=adresu&amp;i=jeszcze&amp;inny=fin<\/em><\/p>\n<p>Niby nie jest to takie skomplikowane, ale niekt\u00f3re sytuacje wydaj\u0105 si\u0119 nie do rozwi\u0105zania.<\/p>\n<p>Uwaga, notka tasiemcowata, wi\u0119c jest spis tre\u015bci. Polecam si\u0119 r\u00f3wnie\u017c uzbroi\u0107 w odpowiedni\u0105 ilo\u015b\u0107 czasu. ;]<\/p>\n<p><!--more--><\/p>\n<ol>\n<li><a href=\"#t1\">Czy\u017cby?<\/a> &#8211; wst\u0119pniak<\/li>\n<li><a href=\"#t2\">Z kopyta<\/a> &#8211; podstawy<\/li>\n<li><a href=\"#t3\">A nie \u0142atwiej?<\/a> &#8211; czy jest sens korzystania\n<li><a href=\"#t4\">Podstawowe praktyczne zastosowania<\/a> &#8211; jo. (jak obok ;))<\/li>\n<ol>\n<li><a href=\"#t41\">Banowanie po IP<\/a><\/li>\n<li><a href=\"#t42\">Blokada przed hotlinkowaniem<\/a> (no troszk\u0119 inna ;))<\/li>\n<li><a href=\"#t43\">www, czy nie www<\/a><\/li>\n<li><a href=\"#t44\">Wymuszanie szyfrowania po\u0142\u0105czenia<\/a><\/li>\n<li><a href=\"#t45\">Z uko\u015bnikiem na ko\u0144cu, czy bez?<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#t5\">Cz\u0119ste pomy\u0142ki<\/a> &#8211; najcz\u0119stsze b\u0142\u0119dy<\/li>\n<li><a href=\"#t6\">Sprytne rozwi\u0105zania dzi\u0119ki mod_rewrite<\/a> &#8211; czy wiesz \u017ce&#8230;?\n<ol>\n<li><a href=\"#t61\">Wydzielenie katalogu <em>public_html<\/em><\/a><\/li>\n<li><a href=\"#t62\">Wirtualne subdomeny<\/a><\/li>\n<li><a href=\"#t63\">Jeden serwis, podobne domeny &#8211; wiele j\u0119zyk\u00f3w<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#t7\">Przypadki beznadziejne &#8211; wy\u017csza szko\u0142a jazdy<\/a> &#8211; co jest pozornie nie do rozwi\u0105zania\n<ol>\n<li><a href=\"#t71\">Przetwarzanie ci\u0105gu po znaku zapytania<\/a><\/li>\n<li><a href=\"#t72\">przepisywanie adresu ze znakami specjalnymi<\/a> &#8211; w tym polskimi<\/li>\n<li><a href=\"#t73\">negocjacja zawarto\u015bci bezpo\u015brednio w Rewrite<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#t8\">Implementacja rzecz straszna &#8211; strona i skrypty<\/a> &#8211; po stronie skrypt\u00f3w i klienta\n<ol>\n<li><a href=\"#t81\">\u0141atwe i szybkie generowanie identyfikator\u00f3w przyjaznych dla URL<\/a><\/li>\n<li><a href=\"#t82\">\u0141atwe korzystanie z cache ca\u0142ych stron<\/a><\/li>\n<li><a href=\"#t83\">Pliki CSS\/obrazki<\/a><\/li>\n<li><a href=\"#t84\">Problemy z Light\/Thick\/Grayboxem i wy\u015bwietlaniem obrazk\u00f3w<\/a><\/li>\n<li><a href=\"#t85\">Zmuszenie formularzy do korzystania z przyjaznych URL-i<\/a><\/li>\n<\/ol>\n<\/li>\n<li><a href=\"#t9\">Epilog<\/a><\/li>\n<\/ol>\n<h3 id=\"t1\">Czy\u017cby?<\/h3>\n<p>Obra\u0142em sobie kiedy\u015b takie osobiste zadanie, aby chocia\u017c spr\u00f3bowa\u0107 zmierzy\u0107 si\u0119 z ka\u017cdym problemem zwi\u0105zanym z przepisywaniem adres\u00f3w na zrozumia\u0142e dla skryptu formy. Rewriting to tak naprawd\u0119 forma zamaskowania tego, co widz\u0105 skrypty wobec postaci widzianej przez go\u015bci i przegl\u0105darki. Jak to wygl\u0105da, patrz: zajawka (teaser).<\/p>\n<p>Zacznijmy od problem\u00f3w, kt\u00f3re wi\u0105\u017c\u0105 si\u0119 z <em>oczywistymi oczywisto\u015bciami<\/em><\/p>\n<h3 id=\"t2\">Z kopyta<\/h3>\n<p>\u017beby m\u00f3c si\u0119 tym jakkolwiek pobawi\u0107, trzeba zrozumie\u0107 par\u0119 rzeczy. Najwa\u017cniejsza, to upewni\u0107 si\u0119, czy nasz serwer\/demon w og\u00f3le umo\u017cliwia wykorzystanie przepisywania adres\u00f3w. Ju\u017c chyba wszystkie hostingi udost\u0119pniaj\u0105 <em>mod_rewrite<\/em> gotowy do u\u017cycia.<\/p>\n<p>Najpopularniejszy jest \u2013 rzecz jasna \u2013 format wprowadzony przez Apache, czyli zapisywanie regu\u0142ek do plik\u00f3w <em>htaccess<\/em>. S\u0105 one bezproblemowo interpretowane przez serwery korzystaj\u0105ce z demona z pi\u00f3rkiem w herbie oraz Litespeed. Niestety, pozosta\u0142e serwery nie obs\u0142uguj\u0105 nadpisywania konfiguracji w <em>htaccess<\/em> i powoduje to konieczno\u015b\u0107 korzystania albo z obej\u015b\u0107 (tzn. przekierowywanie wszystkich \u017c\u0105da\u0144 do skryptu zajmuj\u0105cego si\u0119 tylko obrabianiem regu\u0142ek), albo przepisywanie regu\u0142 na format danego serwera. Nie jest to zbyt trudne, ale cz\u0119sto bywa uci\u0105\u017cliwe chocia\u017cby z powodu dost\u0119pno\u015bci do konfiguracji oraz konieczno\u015bci restartowania demona. Owszem, powstawa\u0142y ju\u017c pewnego rodzaju pomys\u0142y na omini\u0119cie tego problemu (np. <em>htscanner<\/em>), ale nie s\u0105 one jeszcze tak satysfakcjonuj\u0105ce, jakby\u015bmy tego chcieli.<\/p>\n<p>Skupi\u0119 si\u0119 na regu\u0142kach przystosowanych dla Apache\/Litespeed (a to z tej racji, \u017ce nap\u0119dzaj\u0105 wi\u0119kszo\u015b\u0107 rynku hostingowego).<\/p>\n<p>We w\u0142asnym warsztacie dobrze jest <a href=\"http:\/\/eriz.pcinside.pl\/weblog\/instalacja-i-konfiguracja-apache-22-z-php-5x-pod-windows-xp-87.html\">zainstalowa\u0107 Apache<\/a> na w\u0142asnym komputerze i w tym \u015brodowisku zaj\u0105\u0107 si\u0119 testowaniem regu\u0142ek. Mo\u017ce to by\u0107 r\u00f3wnie\u017c dowolny pakiet, kt\u00f3ry zawiera w sobie Indianina (WAMP, XAMP, LAMP, WebServ, itp), trzeba tylko pami\u0119ta\u0107, aby w Linuksie pami\u0119ta\u0107 o skompilowaniu demona z flag\u0105 <em>\u2013enable-rewrite<\/em>, a w Windows odkomentowa\u0107 linijk\u0119 z:<\/p>\n<p><code lang=\"apache\">LoadModule rewrite_module modules\/mod_rewrite.so<\/code><\/p>\n<p>Czytaj: skasowa\u0107 z jej pocz\u0105tku <em>#<\/em>. W niekt\u00f3rych przypadkach b\u0119dzie r\u00f3wnie\u017c konieczne ustawienie dyrektywy <em>AllowOverride All<\/em>. Rzecz jasna, ca\u0142y czas mam na my\u015bli plik <em>httpd.conf<\/em>, po kt\u00f3rego modyfikacji restartujemy demona. Je\u015bli w konfiguracji nie jest nic zepsute, wszystko powinno by\u0107 OK i utworzenie pliku <em>.htaccess<\/em> (samo rozszerzenie) w katalogu <em>htdocs<\/em> z zawarto\u015bci\u0105:<\/p>\n<p><code lang=\"apache\">RewriteEngine On<\/code><\/p>\n<p>nie wysypie nam serwera z b\u0142\u0119dem numer 500 po wywo\u0142aniu strony. Zak\u0142adam, \u017ce wszystko jest w porz\u0105dku. W\u0142a\u015bnie w ten spos\u00f3b zaczniemy zabaw\u0119 z przepisywaniem adres\u00f3w. Wszystko sprowadza si\u0119 do odpowiedniego manewrowania dyrektywami <em>RewriteRule<\/em>.<\/p>\n<p>Przyk\u0142adowy plik <em>htaccess<\/em> mo\u017ce wygl\u0105da\u0107 np. tak:<\/p>\n<p><code lang=\"apache\">RewriteEngine On<\/p>\n<p>RewriteBase \/katalog<br \/>\nRewriteRule ^kategoria\/(.+) kategoria.php?co=$1<\/code><\/p>\n<p>Sk\u0142adnia nie nale\u017cy do szczeg\u00f3lnie skomplikowanych. Podstaw\u0105 przepisywania adres\u00f3w jest znajomo\u015b\u0107 <a href=\"http:\/\/pl.wikipedia.org\/wiki\/Wyra\u017cenie_regularne\">wyra\u017ce\u0144 regularnych<\/a>, kt\u00f3rych om\u00f3wienie jest tematem na jeden albo i na ca\u0142\u0105 seri\u0119 artyku\u0142\u00f3w. Wyra\u017cenia s\u0105 do\u015b\u0107 przydatne, wykorzystywane w wielu sytuacjach. Mo\u017ce kiedy\u015b o nich napisz\u0119. [;<\/p>\n<p>Podstaw\u0105 dyrektyw\u0105, kt\u00f3ra dodaje wyra\u017cenie do puli przetwarzanych, jest <em>RewriteRule<\/em>. Sk\u0142adnia jest nast\u0119puj\u0105ca:<\/p>\n<p><code lang=\"apache\">RewriteRule WYRA\u017bENIE ZAMIENNIK [FLAGI]<\/code><\/p>\n<ul>\n<li>\n<p><em>WYRA\u017bENIE<\/em>, to wyra\u017cenie regularne, kt\u00f3rym zostanie przetestowany adres. Cz\u0119\u015bci, kt\u00f3re zostan\u0105 wykorzystane w zamienniku otaczamy nawiasami.<\/p>\n<\/li>\n<li>\n<p><em>ZAMIENNIK<\/em>, to w\u0142a\u015bciwy adres, kt\u00f3ry zostanie otwarty przez serwer. Aby wykorzysta\u0107 frazy, kt\u00f3re zostan\u0105 podstawione w miejsce nawias\u00f3w w zamienniku, korzystamy z formatu <em>$1<\/em>, <em>$2<\/em> &#8211; liczba oznacza kolejny numer podstawnika w wyra\u017ceniu regularnym. Przyk\u0142adowe zastosowanie poda\u0142em we wcze\u015bniejszym listingu.<\/p>\n<\/li>\n<li>\n<p><em>FLAGI<\/em>, to dyrektywy kt\u00f3rymi zostan\u0105 potraktowane regu\u0142ki. W zasadzie, to przydatnych jest kilka, np.<\/p>\n<ul>\n<li>\n<p><em>[QSA]<\/em>, co jest akronimem od <q>Query String Append<\/q>. Je\u015bli korzystamy z jakichkolwiek formularzy pos\u0142uguj\u0105cych si\u0119 metod\u0105 GET, reflinkami, etc, jest to flaga niezb\u0119dna. Powoduje ona dopisanie do wywo\u0142ywanego skryptu ci\u0105gu zwanego <em>QUERY_STRING<\/em>, czyli wszystkiego po pytajniku w adresie.<\/p>\n<\/li>\n<li>\n<p><em>[L]<\/em> &#8211; oznacza, \u017ce dana regu\u0142a jest ostatni\u0105 do przetwarzania. Wszystkie <em>RewriteRule<\/em> poni\u017cej nie zostan\u0105 wykonane.<\/p>\n<\/li>\n<li>\n<p><em>[NC]<\/em> &#8211; powoduje, \u017ce wyra\u017cenie jest testowane niezale\u017cnie od wielko\u015bci znak\u00f3w w adresie (domy\u015blnie, wyra\u017cenia regularne rozr\u00f3\u017cniaj\u0105 wielkie i ma\u0142e litery)<\/p>\n<\/li>\n<li>\n<p><em>[R]<\/em> &#8211; zamiast ukrycia prawdziwego adresu skryptu, serwer na niego przekierowuje (po ludzku: przegl\u0105darka otwiera go tak, jakby by\u0142 wpisany bezpo\u015brednio do paska adresu). Opcjonalnie przyjmuje kod przekierowania, np. <em>[R=301]<\/em><\/p>\n<\/li>\n<li>\n<p><em>[PT]<\/em> &#8211; do\u015b\u0107 ciekawa, ale rzadko wykorzystywana flaga. Opr\u00f3cz mod_rewrite, s\u0105 r\u00f3wnie\u017c inne modu\u0142y, kt\u00f3re operuj\u0105 na adresie skryptu, jak np. mod_alias, czy inne. Umieszczenie tej flagi powoduje, \u017ce pozosta\u0142e rozszerzenia Apache otrzymaj\u0105 do przetwarzania ju\u017c przepisany adres.<\/p>\n<\/li>\n<\/ul>\n<p>Oczywi\u015bcie flagi mo\u017cna \u0142\u0105czy\u0107 podaj\u0105c je po przecinku, np. <em>[L,NC,QSA]<\/em>.<\/p>\n<\/li>\n<\/ul>\n<p>Jeszcze powinienem wspomnie\u015b o dyrektywnie zwanej <em>RewriteBase<\/em>. W wyra\u017ceniach regularnych s\u0105 dost\u0119pne znaki oznaczaj\u0105ce pocz\u0105tek oraz koniec frazy. Nieraz zdarza si\u0119 tak, i\u017c nasz katalog na stron\u0119 ma URL postaci np. <em>http:\/\/example.org\/~uzyszkodnik\/<\/em> albo dowolny podkatalog wzgl\u0119dem g\u0142\u00f3wnego. Ka\u017cdorazowe dopisywanie <em>~uzyszkodnik<\/em> do wyra\u017cenia by\u0142oby uci\u0105\u017cliwe. I tu pomaga <em>RewriteBase<\/em>, kt\u00f3ra okre\u015bla ten pocz\u0105tek dla wszystkich wyra\u017ce\u0144 w pliku.<\/p>\n<h3 id=\"t3\">A nie \u0142atwiej&#8230;?<\/h3>\n<p>No dobrze, mo\u017cna przecie\u017c ca\u0142y ruch przekierowa\u0107 do jednego skryptu i z jego poziomu dokonywa\u0107 dalszych przekierowa\u0144. Zapytam tylko &#8211; po co? Poza kilkoma niekt\u00f3rymi przypadkami nie ma to sensu, jest to tylko marnowanie mocy na przetwarzanie \u017c\u0105dania przez interpreter skrypt\u00f3w, kt\u00f3ry mo\u017ce odci\u0105\u017cy\u0107 w\u0142a\u015bnie silnik przepisywania adres\u00f3w.<\/p>\n<p>W jakich sytuacjach mo\u017cna pomin\u0105\u0107? W zasadzie zawsze. ;] Chocia\u017cby wy\u015bwietlanie galerii zdj\u0119\u0107, czy te\u017c cache ca\u0142ych stron, ale o tym p\u00f3\u017aniej.<\/p>\n<p>W praktyce jest nieco inaczej &#8211; wiele gotowych skrypt\u00f3w\/bibliotek przerzuca przepisywanie bezpo\u015brednio do w\u0142asnych mechanizm\u00f3w ze wzgl\u0119d\u00f3w przeno\u015bno\u015bci &#8211; aby ten sam skrypt m\u00f3g\u0142 dzia\u0142a\u0107 na jak najwi\u0119kszej liczbie serwer\u00f3w (pami\u0119tajmy, \u017ce jest wiele innych demon\u00f3w, w kt\u00f3rych konfiguracja przepisywania aders\u00f3w jest zupe\u0142nie inna) oraz z innych szczeg\u00f3lnych wzgl\u0119d\u00f3w, o kt\u00f3rych powiem p\u00f3\u017aniej.<\/p>\n<h3 id=\"t4\">podstawowe praktyczne zastosowania<\/h3>\n<p>Tutaj przede wszystkim przyda si\u0119 nam dyrektywa <em>RewriteCond<\/em>. Umo\u017cliwia ona warunkowe wykonanie najbli\u017cszego przepisywania. Sk\u0142adnia?<\/p>\n<p><code lang=\"apache\">RewriteCond %{testowana_zmienna} wyra\u017cenie [FLAGI]<\/code><\/p>\n<ul>\n<li>\n<p><em>testowana_zmienna<\/em> &#8211; jest to jedna z typowych zmiennych dla skrypt\u00f3w. Jest to np. znaczna wi\u0119kszo\u015b\u0107 pozycji z PHP-owej zmiennej predefiniowanej <em>$_SERVER<\/em>, np. IP klienta, przegl\u0105darka, itp. <a href=\"http:\/\/httpd.apache.org\/docs\/2.0\/mod\/mod_rewrite.html#rewritecond\">Pe\u0142na lista<\/a> dost\u0119pna jest na stronach Apache.<\/p>\n<\/li>\n<li>\n<p><em>wyra\u017cenie<\/em> &#8211; wyra\u017cenie regularne, kt\u00f3rego spe\u0142nienie jest jednoznaczne z &#8222;przepuszczeniem&#8221; warunku (warunek zwraca PRAWDA)<\/p>\n<\/li>\n<li>\n<p><em>flagi<\/em> &#8211; tu s\u0105 tylko dwie: <em>NC<\/em> i <em>OR<\/em>. Pierwsza jest analogiczna, co w <em>RewriteRule<\/em> &#8211; ignoruje wielko\u015b\u0107 znak\u00f3w, natomiast druga &#8211; sprawia, \u017ce wyra\u017cenie jest \u0142\u0105czone z nast\u0119pnym przez alternatyw\u0119 (odpowiednik <em>if &#8230; else if &#8230;<\/em>).<\/p>\n<\/li>\n<\/ul>\n<h4 id=\"t41\">Banowanie po IP<\/h4>\n<p>Je\u015bli chodzi o banowanie na poziomie serwera przez <em>deny from all<\/em>, to ma jedn\u0105, ale za to zasadnicz\u0105 wad\u0119 &#8211; uniemo\u017cliwia wy\u015bwietlenie eleganckiego komunikatu dla u\u017cytkownika. Owszem, mo\u017cna skorzysta\u0107 z w\u0142asnych stron b\u0142\u0119d\u00f3w, ale w\u00f3wczas np. nie zapiszemy, kt\u00f3ry konkretnie adres IP nam szczeg\u00f3lnie daje si\u0119 we znaki. Gotow\u0105 list\u0119 adres\u00f3w mo\u017cna pobra\u0107 z np. ze <a href=\"http:\/\/sblam.com\/czarnalista.html\">strony projektu Sblam!<\/a><\/p>\n<p>Tworzymy w\u00f3wczas <em>htaccess<\/em> o mniej wi\u0119cej takiej zawarto\u015bci:<\/p>\n<p><code lang=\"apache\">RewriteEngine On<\/p>\n<p>RewriteCond %{REMOTE_ADDR} ^(255\\.255\\.255\\.255)|(0\\.0\\.0\\.0)$<br \/>\nRewriteCond %{REQUEST_URI} !^ban\\.php$<br \/>\nRewriteRule . ban.php [L,R]<\/code><\/p>\n<p>I teraz \u0142opatologiczne wyja\u015bnienie:<\/p>\n<ol>\n<li>\n<p><strong>Najpierw sprawdzamy, czy IP jest na naszej czarnej li\u015bcie.<\/strong> &#8211; adresy s\u0105 przyk\u0142adowe, zamiast podanych podajemy te, kt\u00f3re chcemy zablokowa\u0107. Kropki <em>escape&#8217;ujemy<\/em>, czyli poprzedzamy odwrotnym uko\u015bnikiem &#8211; aby kropka by\u0142a interpretowana jako kropka, a nie dowolny znak. Nawiasy stanowi\u0105 pogrupowanie wyra\u017ce\u0144, pionowa kreska &#8211; je oddziela.<\/p>\n<\/li>\n<li>\n<p><strong>Sprawdzamy, czy nie banujemy przypadkiem strony z b\u0142\u0119dem.<\/strong> &#8211; gdyby nie sprawdza\u0107, to ca\u0142e nasze dzia\u0142anie nie mia\u0142oby po prostu sensu. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Jak pewnie zauwa\u017cy\u0142e\u015b(a\u015b), wykrzyknik przed wyra\u017ceniem stanowi jego zanegowanie.<\/p>\n<\/li>\n<li>\n<p><strong>Przekierowujemy klienta na stron\u0119 b\u0142\u0119du.<\/strong> &#8211; chyba bez komentarza. ;]<\/p>\n<\/li>\n<\/ol>\n<p>Oczywi\u015bcie rozwi\u0105zanie jest dobre tylko dla niewielkiej liczby adres\u00f3w &#8211; przy wi\u0119kszej ju\u017c lepiej obarczy\u0107 tym wykonywany skrypt.<\/p>\n<p>Ale je\u015bli chcemy wyci\u0105\u0107 ca\u0142e klasy, to nie ma problemu &#8211; mo\u017cemy r\u00f3wnie\u017c u\u017cy\u0107 rewrite:<\/p>\n<p><code lang=\"apache\">RewriteCond %{REMOTE_ADDR} ^(255\\.255\\.255\\.[0-9]+)$<\/code><\/p>\n<p>Wystarczy podmieni\u0107 ze wcze\u015bniejszym <em>RewriteCond<\/em>, z listingu powy\u017cej.<\/p>\n<p>Wycinanie ca\u0142ego IP mo\u017ce by\u0107 bolesne dla sieci osiedlowych. Wielu admin\u00f3w przychodzi nam z pomocn\u0105 d\u0142oni\u0105 i wypuszcza w nag\u0142\u00f3wku <em>X-Forwarded-For<\/em> wewn\u0119trzny adres IP. Wtedy mo\u017cna wyci\u0105\u0107 tylko tego delikwenta, je\u015bli adres jest nam znany.<\/p>\n<p><code lang=\"apache\">RewriteEngine On<\/p>\n<p>RewriteCond %{REMOTE_ADDR} ^(255\\.255\\.255\\.255)|(0\\.0\\.0\\.0)$<br \/>\nRewriteCond %{HTTP:X-Forwarded-For} ^(192\\.168\\.0\\.2)$<br \/>\nRewriteCond %{REQUEST_URI} !^ban\\.php$<br \/>\nRewriteRule . ban.php [L,R]<\/code><\/p>\n<h4 id=\"t42\">Blokada przed hotlinkowaniem<\/h4>\n<p>Transfer jak woda &#8211; nie za darmo, rachunki te\u017c za to s\u0105 naliczane. Wiele os\u00f3b zamiast chocia\u017c przegra\u0107 obrazki do siebie, linkuje bezpo\u015brednio do naszych. Mo\u017cna ten proceder utrudni\u0107 (nie ukr\u00f3ci\u0107).<\/p>\n<p>Najprostsz\u0105 metod\u0105 jest po prostu odci\u0119cie od \u017ar\u00f3d\u0142a &#8211; czyli blokowanie jakichkolwiek \u017c\u0105da\u0144 z obcych stron. Ale czemu by nie wy\u015bwietli\u0107 zamiast tego reklamy? ;] &#8222;Uniwersalna&#8221; by\u0142aby nie do ko\u0144ca OK &#8211; nie wszystkie obrazki przecie\u017c maj\u0105 takie same wymiary. Proste za\u0142o\u017cenie &#8211; sprawdzamy nag\u0142\u00f3wek <em>referer<\/em>, je\u015bli nie ma tam naszej strony &#8211; serwujemy reklam\u00f3wk\u0119. Ale w taki spos\u00f3b, \u017ce pobieramy z poziomu skryptu url do w\u0142a\u015bciwego obrazka, pobieramy wymiary i generujemy w\u0142asny o odpowiednich. Co dalej? Inwencja autora. ;] Oczywi\u015bcie nie jest to rozwi\u0105zanie stuprocentowe &#8211; wiele zap\u00f3r, czy pakiet\u00f3w bezpiecze\u0144stwa po prostu blokuje wysy\u0142anie nag\u0142\u00f3wka <em>http_referer<\/em>.<\/p>\n<p>Ale tak, czy tak &#8211; zawsze b\u0119dzie to utrudnienie. W praktyce? Sprawa jest prosta:<\/p>\n<p><code lang=\"apache\">RewriteEngine On<br \/>\nRewriteCond %{HTTP_REFERER} !http:\/\/example\\.org [NC]<br \/>\nRewriteRule ^(.+)\\.(jpg|gif|jpeg|png)$ hotlink.php?img=$1.$2 [L]<\/code><\/p>\n<p>I teraz wszystko dostajemy w zmiennej <em>$_GET[&#8217;img&#8217;]<\/em>. Co teraz? A powiedzmy, \u017ce co\u015b napiszemy. :]<\/p>\n<p><code lang=\"php\"><?PHP\nif(empty($_GET['img']) OR !file_exists(basename($_GET['img'))){\n    die;\n}\n\n$i = geimagesize(basename($_GET['img']));\n\nif(!$i){\n    die;\n}\n\n$img = imagecreate($i[0], $i[1]);\n$col = imagecolorallocate($im, 0, 0, 0);\n\nimagestring($img, 5, 0, 0, 'prawdopodobnie skradziony obraz, oryginal pochodzi ze strony: http:\/\/example.org');\n\nheader('Content-type: image\/png');\nimagepng($img);\n\n?><\/code><\/p>\n<p>Interpretacja \u015bcie\u017cek, innych &#8211; inwencja autora. Mo\u017cna r\u00f3wnie\u017c wygenerowa\u0107 obrazek i na niego przekierowywa\u0107, mo\u017cliwo\u015bci jest mn\u00f3stwo. Moim zdaniem, najlepiej wygenerowa\u0107 i wys\u0142a\u0107 na jaki\u015b darmowy hosting w celu oszcz\u0119dzenia transferu. :]<\/p>\n<h4 id=\"t43\">www, czy nie www?<\/h4>\n<p>To pytanie odwiecznie wzbudza kontrowersje. Ja jestem tego samego zdania, co autorzy kampani <a href=\"http:\/\/no-www.org\">no-www<\/a> &#8211; <em>www<\/em>, to nie jest \u017caden protok\u00f3\u0142, ludziom ju\u017c od dawna si\u0119 jednoznacznie kojarzy ci\u0105g <em>strona.com<\/em> ze stron\u0105 w Sieci.<\/p>\n<p>Przyzwyczajenie jednak drug\u0105 natur\u0105 cz\u0142owieka i nie mo\u017cna takiego odci\u0105\u0107 od naszych stron. Ciekawie robi np. last.fm, kt\u00f3ry przekierowuje na preferowan\u0105 przez autor\u00f3w wersj\u0119.<\/p>\n<p>Tu wystarczy prosta regu\u0142ka:<\/p>\n<p><code lang=\"apache\">RewriteEngine On<\/p>\n<p>RewriteCond %{HTTP_HOST} ^www\\.(.+)$ [NC]<br \/>\nRewriteRule ^(.*)$ http:\/\/%1\/$1 [R=301,L]<\/code><\/p>\n<p>Aby pozby\u0107 si\u0119 za ka\u017cdym razem <em>www<\/em> w adresie. Kod zapo\u017cyczony ze strony kampanii no-www. Aby zrobi\u0107 na odwr\u00f3t, zmodyfikujemy lekko regu\u0142ki:<\/p>\n<p><code lang=\"apache\">RewriteEngine On<br \/>\nRewriteCond %{HTTP_HOST} !^www\\.(.+)$ [NC]<br \/>\nRewriteRule ^(.*)$ http:\/\/www.%1\/$1 [R=301,L]<\/code><\/p>\n<p>Jak pewnie zauwa\u017cy\u0142e\u015b(a\u015b), skorzystano tutaj z warto\u015bci z wyra\u017cenia przetwarzanego w <em>RewriteCond<\/em>, tym zajmiemy si\u0119 p\u00f3\u017aniej, jak to praktycznie wykorzysta\u0107.<\/p>\n<h4 id=\"t44\">wymuszanie szyfrowania po\u0142\u0105czenia<\/h4>\n<p>Bezpiecze\u0144stwo w dzisiejszych czasach jest kwesti\u0105 coraz wa\u017cniejsz\u0105. Certyfikat SSL dla strony nie jest dzisiaj ju\u017c wydatkiem fortuny, jak to by\u0142o jeszcze par\u0119 lat temu.<\/p>\n<p>Oczywi\u015bcie nie ma sensu by\u0107 kim\u015b przewra\u017cliwionym i szyfrowanie danych dost\u0119pu publicznie jest tylko marnowaniem mocy procesora serwera. Zaczniemy jednak od w\u0142\u0105czenia SSL dla strony rejestracji\/logowania u\u017cytkownika:<\/p>\n<p><code lang=\"apache\">RewriteCond %{HTTPS} off<br \/>\nRewriteRule ^\/(rejestracja|logowanie) https:\/\/%{HTTP_HOST}%{REQUEST_URI} [R,L]<\/code><\/p>\n<p>I rejestracja\/logowanie b\u0119dzie przebiega\u0142o przez bezpieczne po\u0142\u0105czenie z serwerem. Dobrze by by\u0142o wymusi\u0107 wy\u015bwietlanie obrazk\u00f3w oraz pozosta\u0142ych strony przez zwyk\u0142y HTTP.<\/p>\n<p><code lang=\"apache\">RewriteCond %{HTTPS} on<br \/>\nRewriteRule ^\/(^[rejestracja|logowanie]) http:\/\/%{HTTP_HOST}%{REQUEST_URI} [R,L]<\/p>\n<p>RewriteCond %{HTTPS} on<br \/>\nRewriteCond %{REQUEST_URI} \\.(gif|png|jpg|css|js)$<br \/>\nRewriteRule (.*) http:\/\/%{HTTP_HOST}%{REQUEST_URI} [R,L]<br \/>\n<\/code><\/p>\n<p>I to tyle, je\u015bli chodzi o SSL.<\/p>\n<h4 id=\"t45\">z uko\u015bnikiem na ko\u0144cu, czy bez?<\/h4>\n<p>Kolejna kwestia sp\u0119dzaj\u0105ca nieraz sen z powiek osobie, kt\u00f3ra pr\u00f3buje si\u0119 pozby\u0107 uko\u015bnika na ko\u0144cu. Na pocz\u0105tku trzeba ustali\u0107 jedn\u0105 rzecz &#8211; nie da si\u0119, powtarzam &#8211; nie da si\u0119 usun\u0105\u0107 slesza na ko\u0144cu \u015bcie\u017cki, je\u015bli istnieje katalog o tej nazwie. Pr\u00f3bowa\u0142em na wszelakie mo\u017cliwe sposoby, pyta\u0142em i wniosek &#8211; nie da si\u0119 (je\u015bli jednak si\u0119 da &#8211; b\u0119dzie fajnie, gdy mnie kto\u015b o\u015bwieci ;)). Jedyne wyj\u015bcie &#8211; zaprefiksowanie nazwy katalogu i subtelne przekierowywanie \u017c\u0105da\u0144.<\/p>\n<p>Na co trzeba uwa\u017ca\u0107? \u017beby nie wpa\u015b\u0107 w zap\u0119tlone przekierowanie. ;]<\/p>\n<p><code lang=\"apache\">RewriteCond %{REQUEST_URI} (.*)\/$<br \/>\nRewriteCond %{REQUEST_FILENAME} !-d<br \/>\nRewriteRule ^([a-zA-Z\\-]+)\/$ \/$1 [R=301,QSA,L]<\/code><\/p>\n<p>I powinno by\u0107 ok. ;]<\/p>\n<h3 id=\"t5\">cz\u0119ste pomy\u0142ki<\/h3>\n<p>Podstawowe triki ju\u017c om\u00f3wi\u0142em, teraz pora na najcz\u0119stsze b\u0142\u0119dy przy przepisywaniu adres\u00f3w. Wi\u0119kszo\u015b\u0107 w oparciu o problemy poruszane na <a href=\"http:\/\/forum.php.pl\">Polskim Forum PHP<\/a>. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/><\/p>\n<ol>\n<li><strong>Zamiana parametr\u00f3w w <em>RewriteRule<\/em><\/strong>.\n<p>Zdarzy\u0142 si\u0119 jeden taki przypadek, \u017ce kto\u015b pisz\u0105c regu\u0142k\u0119 zamieni\u0142 miejscami wzorzec z zamiennikiem. Pami\u0119taj: sk\u0142adnia <em>RewriteRule<\/em> zawsze jest nast\u0119puj\u0105ca:<\/p>\n<p><code lang=\"apache\">RewriteRule WZORZEC ZAMIENNIK [FLAGI]<\/code><\/p>\n<p>I nigdy inna. Problem mo\u017ce cz\u0119sto wyst\u0119powa\u0107 przy pr\u00f3bie uwzgl\u0119dniania spacji jako wzorca. Spacja jest znakiem rozdzielaj\u0105cym wzorzec z zamiennikiem i nale\u017cy j\u0105 zawsze poprzedzi\u0107 odwr\u00f3conym uko\u015bnikiem, aby j\u0105 uwzgl\u0119dni\u0107 czy to we wzorcu, czy w zamienniku.<\/p>\n<\/li>\n<li><strong>Przej\u015bcie na przyjazne adresy i zamaina link\u00f3w<\/strong>.\n<p>Ok, regu\u0142ki s\u0105, testy si\u0119 udaj\u0105, ale jest pewien prozaiczny problem &#8211; w kodzie linki si\u0119 same nie zmieni\u0105. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Musisz je zmieni\u0107 r\u0119cznie na now\u0105 konwencj\u0119. Owszem, mo\u017cna skorzysta\u0107 z buforowania wyj\u015bcia w PHP i je zamieni\u0107 korzystaj\u0105c z wyra\u017ce\u0144 regularnych, ale jest to nieco bez sensu. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Gdy ju\u017c zamienisz linki, dobr\u0105 praktyk\u0105 jest powiadomienie wyszukiwarek, kt\u00f3ry rodzaj odno\u015bnik\u00f3w powinny wy\u015bwietla\u0107 w wynikach wyszukiwarki. O tym pod koniec notki.<\/p>\n<\/li>\n<li><strong>Zasi\u0119g <em>RewriteCond<\/em><\/strong>.\n<p>Wiele os\u00f3b zapomina o pewnej do\u015b\u0107 istotnej kwestii &#8211; wszystkie <em>RewriteCond<\/em> obowi\u0105zuj\u0105 tylko do <strong>najbli\u017cszego <em>RewriteRule<\/em><\/strong>. Nast\u0119pne wykonaj\u0105 si\u0119 ju\u017c bezwarunkowo. Rozwi\u0105za\u0144 tego problemu jest kilka:<\/p>\n<ol>\n<li>\n<p>Powt\u00f3rzy\u0107 bloki z <em>RewriteCond<\/em> tyle razy, ile korzystamy z <em>RewriteRule<\/em> (przy dw\u00f3ch, trzech jest zno\u015bnie, przy wi\u0119kszej ilo\u015bci &#8211; masochizm)<\/p>\n<\/li>\n<li>\n<p>Zmodyfikowa\u0107 testowane wyra\u017cenia tak, aby zbi\u0107 je do jednego (patrz: przyk\u0142ad z adresami IP &#8211; nie rozdziela\u0142em adres\u00f3w IP, tylko je \u0142\u0105czy\u0142em). Niestety, nie da si\u0119 tak ze wszystkim, gdy\u017c testowane zmienne mog\u0105 by\u0107 r\u00f3\u017cne.<\/p>\n<\/li>\n<li>\n<p>Przerzuci\u0107 obs\u0142ug\u0119 przepisywanych adres\u00f3w bezpo\u015brednio do skryptu sprawdzaj\u0105c jedynie, czy istniej\u0105 pliki\/katalogi, czy nie. O tym ju\u017c na koniec artyku\u0142u.<\/p>\n<\/li>\n<\/ol>\n<\/li>\n<li><strong>Zastosowanie wsz\u0119dzie tych samych wzorc\u00f3w<\/strong>.\n<p>To w\u0142a\u015bciwie problem konstrukcji\/rozumienia sk\u0142adni wyra\u017ce\u0144 regularnych. Rozwa\u017cmy przyk\u0142ad:<\/p>\n<p><code lang=\"apache\">RewriteRule ^index,([0-9]),([a-zA-Z]+),([a-zA-Z]+),([a-zA-Z]+)$ index.php?set=$1&id=$2&get=$3&show=$4<br \/>\nRewriteRule ^index,([0-9]),([a-zA-Z]+),([a-zA-Z]+),([a-zA-Z]+)$ index.php?set=$1&id=$2&get=$3&pokaz=$5<br \/>\nRewriteRule ^index,([0-9]),([a-zA-Z]+),([a-zA-Z]+),([a-zA-Z]+)$ index.php?set=$1&id=$2&get=$3&polecamy=$6<\/code><\/p>\n<p>Pytanie by\u0142o na forum &#8211; dlaczego przetwarza mi tylko pierwsz\u0105 regu\u0142k\u0119? Ale\u017c odpowied\u017a jest prosta &#8211; sk\u0105d Rewrite ma wiedzie\u0107, kt\u00f3re regu\u0142ka jest przypisana do konkretnego dzia\u0142u? [; Trzeba w jaki\u015b spos\u00f3b to rozr\u00f3\u017cni\u0107:<\/p>\n<p><code lang=\"apache\">RewriteRule ^index,([0-9]),([a-zA-Z]+),([a-zA-Z]+),show$ index.php?set=$1&id=$2&get=$3&show=show<br \/>\nRewriteRule ^index,([0-9]),([a-zA-Z]+),([a-zA-Z]+),pokaz$ index.php?set=$1&id=$2&get=$3&pokaz=pokaz<br \/>\nRewriteRule ^index,([0-9]),([a-zA-Z]+),([a-zA-Z]+),polecamy$ index.php?set=$1&id=$2&get=$3&polecamy=polecamy<\/code><\/p>\n<p>I teraz jest ok.<\/p>\n<\/li>\n<li><strong>Stosowanie kropek we wzorcach<\/strong>.\n<p>Ponownie program zwi\u0105zany bardziej z wyra\u017ceniami regularnymi. Kropka jest sk\u0142adnikiem wzorca dopasowuj\u0105cym dowolny znak. Wystarczy poprzedzi\u0107 j\u0105 backslashem (<em>\\.<\/em>) i b\u0119dzie ok. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_smile.png\" alt=\":)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/><\/p>\n<\/li>\n<li><strong>B\u0142\u0119dne przetwarzanie liczby argument\u00f3w<\/strong>\n<p>Za\u0142\u00f3\u017cmy \u017ce mamy podzia\u0142 <em>\/strona\/podstrona<\/em> i nast\u0119puj\u0105ce regu\u0142y:<br \/>\n<code lang=\"apache\">RewriteRule (.+) index.php?strona=$1 [L]<br \/>\nRewriteRule (.+)\/(.+) index.php?strona=$1&podstrona=$2 [L]<\/code><\/p>\n<p>Co jest nie tak? Podstrony nigdy nie wywo\u0142amy w ten spos\u00f3b. Dlaczego? Gdy\u017c pierwsze wyra\u017cenie b\u0119dzie pasowa\u0142o zar\u00f3wno do linku z podstron\u0105 jak i tej bez. Dlatego trzeba zamieni\u0107 regu\u0142y miejscami tak, aby najwi\u0119ksza liczba przetwarzanych parametr\u00f3w by\u0142a interpretowana na samym pocz\u0105tku.<\/p>\n<p><code lang=\"apache\">RewriteRule (.+)\/(.+) index.php?strona=$1&podstrona=$2 [L]<br \/>\nRewriteRule (.+) index.php?strona=$1 [L]<\/code>\n<\/li>\n<\/ol>\n<h3 id=\"t6\">sprytne rozwi\u0105zania dzi\u0119ki mod_rewrite<\/h3>\n<p>Przepisywanie adres\u00f3w, to nie tylko spos\u00f3b na lepsze zaprezentowanie URL, ale tak\u017ce spos\u00f3b na podniesienie funkcjonalno\u015bci serwisu, czy te\u017c logiki aplikacji. Zreszt\u0105 &#8211; zobacz. (;<\/p>\n<h4 id=\"t61\">Wydzielenie katalogu <em>public_html<\/em><\/h4>\n<p>Niestety, zdarzaj\u0105 si\u0119 nadal prymitywne hostingi, kt\u00f3re wszystkie wgrywane przez FTP pliki wrzucaj\u0105 do katalogu publicznego. Jakie to niesie za sob\u0105 skutki? Chyba nie trzeba m\u00f3wi\u0107 &#8211; mniejsze bezpiecze\u0144stwo skrypt\u00f3w, trzeba pilnowa\u0107 newralgicznych danych.<\/p>\n<p>M\u0119cz\u0105c si\u0119 z <a href=\"http:\/\/cakephp.org\/\">CakePHP<\/a> w jego pliku <em>htaccess<\/em> natrafi\u0142em na fajne rozwi\u0105zanie:<\/p>\n<p><code lang=\"apache\">RewriteEngine On<br \/>\nRewriteRule ^(.*)$ public_html\/$1<\/code><\/p>\n<p>Jedna linijka i mamy katalog <em>public_html<\/em>. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_smile.png\" alt=\":)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Wszystkie pozosta\u0142e regu\u0142ki wrzucamy do osobnego <em>htaccess<\/em> znajduj\u0105cego si\u0119 w katalogu publicznym.<\/p>\n<h4 id=\"t62\">wirtualne subdomeny<\/h4>\n<p>Widzia\u0142e\u015b(a\u015b) pewnie nieraz serwisy korzystaj\u0105ce z konwencji <em>u\u017cytkownik.strona.pl<\/em>. Nie, to nie s\u0105 osobno tworzone subdomeny. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Z takich adres\u00f3w korzysta np. <a href=\"http:\/\/blip.pl\">BLIP<\/a>. Jest tak naprawd\u0119 jedna kopia skryptu, nikt niczego przy nowej rejestracji nie podpina, wszystki dzieje si\u0119 automatycznie.<\/p>\n<p>Trzeba przede wszystkim zacz\u0105\u0107 od konfiguracji DNS w hostingu\/na serwerze. Dana domena musi miec uaktywniony tzw. <em>wildcard<\/em>, czyli opcj\u0119, kt\u00f3ra nakazuje serwerowi u\u017cycie zawarto\u015bci z g\u0142\u00f3wnej domeny dla ka\u017cdej nieistniej\u0105cej domeny. Je\u015bli nie ma takiej opcji w panelu administracyjnym, wystarczy zazwyczaj mail do administratora. Je\u015bli korzystamy z serwera dedykowanego\/w\u0142asnego &#8211; <a href=\"http:\/\/www.debian-administration.org\/article\/Wildcard_hosting_with_Apache_and_Bind\">modyfikujemy pliki binda oraz Apache<\/a>.<\/p>\n<p>Wtedy wystarczy ju\u017c odpowiedni <em>htaccess<\/em> w katalogu g\u0142\u00f3wnej domeny:<\/p>\n<p><code lang=\"apache\">RewriteCond %{HTTP_HOST} ^([^.]+)\\.example\\.org [NC]<br \/>\nRewriteRule ^(.*) http:\/\/example.org\/?user=%1 [QSA]<\/code><\/p>\n<p>I w parametrze <em>$_GET[&#8217;user&#8217;]<\/em> b\u0119dziemy mogli zidentyfikowa\u0107 u\u017cytkownika na podstawie subdomeny.<\/p>\n<h4 id=\"t63\">jeden serwis, podobne domeny &#8211; wiele j\u0119zyk\u00f3w<\/h4>\n<p>Nasza firma dzia\u0142a mi\u0119dzynarodowo, serwis posiada kilka wersji j\u0119zykowych. Mamy domeny <em>firma.pl<\/em>, <em>firma.com<\/em>, <em>firma.co.uk<\/em>, <em>firma.ru<\/em>. Dlaczego tworzy\u0107 osobne wersje stron dla ka\u017cdego j\u0119zyka? Nie jest to mo\u017ce za bardzo SEO-przyjazne, ale koncepcyjnie &#8211; da si\u0119. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Zawsze mo\u017cna to wykorzysta\u0107 w innym celu:<\/p>\n<p><code>RewriteCond %{http_host} firma\\.([a-z\\.0-9]+)$ [NC]<br \/>\nRewriteRule ^(.*) http:\/\/firma.com\/?lang=%1 [R=301,L,QSA]<\/code><\/p>\n<p>I w zale\u017cno\u015bci od oddzia\u0142u jeste\u015bmy przekierowywani na stron\u0119 centrali firmy z odpowiednio ustawionym j\u0119zykiem. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_smile.png\" alt=\":)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/><\/p>\n<h3 id=\"t7\">przypadki beznadziejne &#8211; wy\u017csza szko\u0142a jazdy<\/h3>\n<p>Niekt\u00f3re sytuacje pozornie wydaj\u0105 si\u0119 beznadziejne, ale to nie znaczy, \u017ce nie da si\u0119 ich w og\u00f3le rozwi\u0105za\u0107. :] Jak to mawiaj\u0105, <q>zadanie nierozwi\u0105zywalne jest tylko pozornie nie do rozwi\u0105zania &#8211; wymaga jedynie wi\u0119kszego nak\u0142adu czasu<\/q>.<\/p>\n<h4 id=\"t71\">przetwarzanie ci\u0105gu po znaku zapytania<\/h4>\n<p>Zmieniamy konwencj\u0119 odno\u015bnik\u00f3w na stronie, ale zale\u017cy nam na szybkim uwzgl\u0119dnieniu tego w wyszukiwarkach. Chcemy brzydkie adresy zamieni\u0107 na te \u0142adniejsze r\u00f3wnie\u017c w wynikach wyszukiwania.<\/p>\n<p>Technicznie adresy b\u0119d\u0105 nadal rozpoznawane &#8211; to w wi\u0119kszo\u015bci przypadk\u00f3w nie b\u0119dzie stanowi\u0142o problemu. Trzeba jednak powiadomi\u0107 wyszukiwarki, \u017ce zmiana nast\u0105pi\u0142a. Je\u015bli stary schemat adres\u00f3w r\u00f3wnie\u017c polega\u0142 na mod_rewrite, nie b\u0119dzie problemu. Gorzej, gdy jest to konwencja bazuj\u0105ca na tzw. <em>QUERY_STRING<\/em>, czyli w stylu <em>index.php?strona=asd&amp;dzial=xyz<\/em>.<\/p>\n<p>Wiele os\u00f3b pr\u00f3buje bezpo\u015brednio poszukiwa\u0107 odpowiedniego ci\u0105gu przez <em>RewriteRule<\/em>. Niestety, nie ma to prawa zadzia\u0142a\u0107, gdy\u017c ci\u0105g po znaku zapytania jest dopisywany po adresu dopiero po operacji maskowania. Trzeba wi\u0119c pos\u0142u\u017cy\u0107 si\u0119 inn\u0105 metod\u0105:<\/p>\n<p><code lang=\"apache\">RewriteCond %{QUERY_STRING} strona=([^&;]*)<br \/>\nRewriteCond %{QUERY_STRING} dzial=([^&;]*)<br \/>\nRewriteRule . \/%1\/%2 [R=301]<\/code><\/p>\n<p>Czemu takie &#8222;dziwne&#8221; wzorce? Jest to tzw. negacja znak\u00f3w, czyli innymi s\u0142owy <em>wszystkie opr\u00f3cz znaku &#8222;&amp;&#8221; i \u015brednika<\/em>. S\u0105 to separatory \u015bcie\u017cki, wi\u0119c trzeba wszystko wydzieli\u0107, gdy\u017c parametr\u00f3w mo\u017ce by\u0107 kilka. W ten spos\u00f3b wyszukiwarki b\u0119d\u0105 wiedzia\u0142y od najbli\u017cszego przeindeksowania, kt\u00f3r\u0105 wersj\u0119 adresu wy\u015bwietli\u0107.<\/p>\n<h4 id=\"t72\">przepisywanie adresu ze znakami specjalnymi<\/h4>\n<p>O ile znaki z alfabetu plus cyfry\/podkre\u015blenia\/plusy nie s\u0105 problemem przy przepisywaniu, to nieraz zdarza si\u0119 tak, \u017ce zachodzi konieczno\u015b\u0107 skorzystania ze znak\u00f3w specjalnych w adresach (patrz: adresy w Wikipedii).<\/p>\n<p>Popatrzmy chocia\u017c na nawiasy, czy spacje w adresach. W wyra\u017ceniach regularnych s\u0105 one odpowiednio znakami specjalnymi, a w Rewritingu &#8211; separatorem wzorca i zamiennika. Aby bezproblemowo ich u\u017cy\u0107, trzeba poprzedzi\u0107 je odwrotnym uko\u015bnikiem:<\/p>\n<p><code lang=\"apache\">RewriteRule ^dzial\\((a-z)+\\) index.php?dzial=$1 [L]<\/code><\/p>\n<p>Osobnym tematem s\u0105 polskie znaki. Tu zaczynaj\u0105 si\u0119 schody, kt\u00f3re wynikaj\u0105 z pewnych (b\u0142\u0119dnych) za\u0142o\u017ce\u0144:<\/p>\n<ul>\n<li>\n<p><strong>polskie znaki zawieraj\u0105 si\u0119 w klasie <em>[a-z]<\/em><\/strong> &#8211; b\u0142\u0105d! Dla nas to oczywiste, \u017ce polskie diakrytyki maj\u0105 swoje miejsce w alfabecie, ale czy wyra\u017cenia regularne, to produkt polski? Nie &#8211; co by by\u0142o np. w sytuacji Cyrylicy? Spowodowa\u0142oby to ogromny ba\u0142agan. Jednak wzorzec dowolnego znaku (kropka) pozwala na przes\u0142anie naszych rodzimych literek dalej.<\/p>\n<\/li>\n<li>\n<p><strong>wszystkie przegl\u0105darki wysy\u0142aj\u0105 polskie znaki w tym samym kodowaniu<\/strong> &#8211; niestety &#8211; prawda jest okrutna. Jedna przegl\u0105darka wy\u015ble w utf-8, inna w iso-8859-2, a jeszcze inna rozbije na jakie\u015b dziwne znaki. Trzeba sprawdza\u0107 przegl\u0105dark\u0119 go\u015bcia i dokonywa\u0107 odpowiednich przekierowa\u0144. St\u0105d odpada r\u0119czne wpisywanie polskich znak\u00f3w do zakres\u00f3w (np. <em>[a-z\u0105\u0107\u0119\u0142\u00f3\u0144\u015b\u017a\u017c]<\/em>)<\/p>\n<\/li>\n<\/ul>\n<p>W\u00f3wczas trzeba albo skorzysta\u0107 ze sprawdzania przegl\u0105darki poprzez powielenie zestawu regu\u0142 z innym kodowaniem, albo przekierowanie do skryptu i testowanie.<\/p>\n<p>Uniwersalne przekierowanie dla wszystkich skrypt\u00f3w jest do\u015b\u0107 proste:<\/p>\n<p><code lang=\"apache\">RewriteEngine On<br \/>\nRewriteCond %{REQUEST_FILENAME} !-f<br \/>\nRewriteRule . index.php [L]<\/code><\/code><\/p>\n<p>Po co jest ten <em>RewriteCond<\/em>? Ano po to, aby serwer normalnie wysy\u0142a\u0142 obrazki, a nie obarcza\u0142 tym zadaniem skrypt. ;]<\/p>\n<h4 id=\"t73\">negocjacja zawarto\u015bci bezpo\u015brednio w Rewrite<\/h4>\n<p>Ilu \u0142ebmajstr\u00f3w si\u0119 napoci\u0142o przy tym, aby w jaki\u015b spos\u00f3b sprawdza\u0107, w jakim MIME wysy\u0142a\u0107 dokumenty HTML&#8230; <em>application\/xhtml+xml<\/em>, czy <em>text\/html<\/em>? Fakt &#8211; niekt\u00f3re przegl\u0105darki wprowadzaj\u0105 obostrzenia.<\/p>\n<p>Czasem nie ma to znaczenia, a i statyczne pliki powinny mie\u0107 negocjacj\u0119 zawarto\u015bci (np. cache pe\u0142nych stron). Owszem, mo\u017cna osobny skrypt, ale jaki jest sens czego\u015b takiego, skoro Apache jest w stanie wykona\u0107 to do\u015b\u0107 sprawnie? ;]<\/p>\n<p><code lang=\"apache\">RewriteCond %{REQUEST_URI} -f<br \/>\nRewriteCond %{REQUEST_URI} \\.htm<br \/>\nRewriteCond %{HTTP:Accept} application\/xhtml+xml<br \/>\nRewriteRule (.+) $1 [T=application\/xhtml+xml]<\/code><\/p>\n<h3 id=\"t8\">implementacja rzecz straszna &#8211; strona i skrypty<\/h3>\n<p>Rewrite, to nie tylko kwestia serwera &#8211; to tak\u017ce pewne r\u00f3\u017cnice w konstrukcji skrypt\u00f3w i samych stron. Tu te\u017c mo\u017cna pope\u0142ni\u0107 nieco b\u0142\u0119d\u00f3w i to wcale nie takich oczywistych. Ale o tym p\u00f3\u017aniej. ;]<\/p>\n<h4 id=\"t81\">\u0142atwe i szybkie generowanie identyfikator\u00f3w przyjaznych dla URL<\/h4>\n<p>Zadanie jest niby proste: z podanego ci\u0105gu znak\u00f3w (np. tytu\u0142u) mamy utworzy\u0107 identyfikator, kt\u00f3ry nie b\u0119dzie encjowany (czytaj: wszystkie znaki nie zamieni\u0105 si\u0119 na stos procent\u00f3w i liczb).<\/p>\n<p>Co robimy? Polskie znaki zamieniamy na \u0142aci\u0144skie odpowiedniki, spacje na my\u015blniki.<\/p>\n<p><code lang=\"php\">function ident($string){<br \/>\n        $string = str_replace(' ', '-', $string);<br \/>\n        $string = iconv('utf-8', 'ascii\/\/translit', $string);<br \/>\n        $string = preg_replace('#[^a-z0-9\\-\\.]#si', '', $string);<br \/>\n        return str_replace('\\'', '', $string);<br \/>\n    }<\/code><\/p>\n<p>Jak to dzia\u0142a? A spr\u00f3buj sam(a). ;]<\/p>\n<h4 id=\"t82\">\u0142atwe korzystanie z cache ca\u0142ych stron<\/h4>\n<p>S\u0142ysza\u0142e\u015b(a\u015b) pewnie o wtyczce WP-SuperCache. W wersji ekstremalnej buforuje ona ca\u0142e strony tak, \u017ce do go\u015bcia trafia wersja odczytana bezpo\u015brednio z pliku. Mo\u017cemy i u siebie zrobi\u0107 co\u015b podobnego. [;<\/p>\n<p>Idea jest do\u015b\u0107 prosta, wr\u0119cz \u0142opatologiczna. Zak\u0142adam, \u017ce mamy schemat link\u00f3w <em>\/dzial<\/em>, strony zmieniaj\u0105 si\u0119 bardzo rzadko. Przy ka\u017cdym niezbuforowanym dzia\u0142aniu skryptu, cache&#8217;ujemy zawarto\u015b\u0107 do pliku odpowiadaj\u0105cego strukturze link\u00f3w, np. <em>\/dzial.htm<\/em>. Kasujemy albo przy zmianie zawarto\u015bci, albo przy pomocy bota uruchamianego przez crona. Jak wygl\u0105da\u0142oby to przy Rewrite?<\/p>\n<p><code lang=\"apache\">RewriteEngine On<\/p>\n<p>RewriteCond %{REQUEST_URI} [^\\.\/\\\\] [NC]<br \/>\nRewriteCond %{REQUEST_URI}.htm -f [NC]<br \/>\nRewriteRule ^(.+)$ $1.htm [L]<\/code><\/p>\n<p>I gotowe. [;<\/p>\n<h4 id=\"t83\">pliki CSS\/obrazki<\/h4>\n<p>B\u0142\u0105d cz\u0119sto pope\u0142niany przez pocz\u0105tkuj\u0105cych &#8211; linki s\u0105 skonstruowane wg konwencji katalogowej, np. <em>dzial\/strona<\/em>. Na stronie g\u0142\u00f3wnej (nie zawsze, zale\u017cy ;)) wszystko jest ok, ale w poddzia\u0142ach ju\u017c znika formatowanie, obrazki r\u00f3wnie\u017c si\u0119 nie wy\u015bwietlaj\u0105.<\/p>\n<p>Zacznijmy od przyczyny. Ustalmy jedn\u0105 kwesti\u0119 &#8211; przegl\u0105darka NIE WIE, \u017ce strona u\u017cywa przepisywania adres\u00f3w. \u0141apiesz? Ok, inaczej &#8211; na stronie \u015bcie\u017ck\u0119 do CSS masz podan\u0105 mniej wi\u0119cej tak: <em>css\/style.css<\/em>. Gdy otworzysz stron\u0119 <em>dzial<\/em>, przegl\u0105darka szuka stylu CSS w katalogu <em>dzial\/css\/style.css<\/em>.<\/p>\n<p>Rozwi\u0105za\u0144 jest kilka, najpierw skrytykuj\u0119 najgorsze. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_laugh.png\" alt=\":D\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Mianowicie niekt\u00f3rzy pisz\u0105 regu\u0142y tak, \u017ceby w ka\u017cdym z tych pseudokatalog\u00f3w istnia\u0142 plik ze stylami\/obrazki. Niby wszystko dzia\u0142a, ale&#8230; Pozostaje jeszcze kwestia transferu. Ot\u00f3\u017c przy takim rozwi\u0105zaniu problemu przegl\u0105darka pobiera ten sam plik <strong>OSOBNO<\/strong> dla ka\u017cdego dzia\u0142u. O cache mo\u017cna raczej pomarzy\u0107. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_tongue.png\" alt=\":P\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/><\/p>\n<p>Natomiast je\u015bli chodzi o poprawne rozwi\u0105zania, to istniej\u0105 dwa:<\/p>\n<ol>\n<li><strong>korzystanie z tagu &lt;base \/&gt;<\/strong><code lang=\"html\"><base href=\"http:\/\/strona.pl\" \/><\/code><\/li>\n<li>\n<p><strong>\u015bcie\u017cki bezwzgl\u0119dne<\/strong><\/p>\n<p>np. <em>http:\/\/example.org\/css\/style.css<\/em> albo <em>\/css\/style.css<\/em><\/p>\n<\/li>\n<\/ol>\n<p>Kt\u00f3re lepsze? Do wyboru, do koloru. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/> Osobi\u015bcie preferuj\u0119 drugie, a to z powodu, \u017ce stosuj\u0119 czasem linki wzgl\u0119dne do podstrron. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_wink.png\" alt=\";)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/><\/p>\n<h4 id=\"t84\">problemy z Light\/Thick\/Grayboxem i wy\u015bwietlaniem obrazk\u00f3w<\/h4>\n<p>Zacznijmy od tego, w jaki spos\u00f3b *box w og\u00f3le dzia\u0142a. Ca\u0142a maszyneria podpinana jest tak, \u017ce typ otwieranej zawarto\u015bci jest ustalany na podstawie rozszerzenia. Je\u015bli nie ma go podanego &#8211; zazwyczaj skrypt tworzy &lt;iframe \/&gt; o sztywnych wymiarach. Czym to skutkuje &#8211; nie musz\u0119 m\u00f3wi\u0107 &#8211; obci\u0119te zdj\u0119cia, brak mo\u017cliwo\u015bci skorzystania z opcji galerii\/pokazu slajd\u00f3w.<\/p>\n<p>Je\u015bli na stronie zdj\u0119cia posiadaj\u0105 \u015bcie\u017cki w konwencji <em>\/galeria\/zdjecia\/tytul-1<\/em>, to nie ma si\u0119 czemu dziwi\u0107 &#8211; *box nie sprawdza MIME zawarto\u015bci i wrzuca wszystko do jednego wora &#8211; jakby to by\u0142a zwyczajna strona. Dlaczego? Ano brak rozszerzenia pliku. Co z tym zrobi\u0107?<\/p>\n<ul>\n<li>\n<p>Zmodyfikowa\u0107 regu\u0142ki tak, aby nazwy zdj\u0119\u0107 ko\u0144czy\u0142y si\u0119 rozszerzeniem albo&#8230;<\/p>\n<\/li>\n<li>\n<p>zmieni\u0107 skrypt *boksa. Dodajemy w\u0142asne regu\u0142y sprawdzania zawarto\u015bci (np. wyra\u017cenie regularne na URL).<\/p>\n<\/li>\n<\/ul>\n<p>Drugie rozwi\u0105zanie wymaga znajomo\u015bci skryptu, w dodatku &#8211; przy najbli\u017cszej aktualizacji *boksa b\u0119dziemy musieli zabra\u0107 si\u0119 do roboty od nowa&#8230;<\/p>\n<h4 id=\"t85\">zmuszenie formularzy do korzystania z przyjaznych URL-i<\/h4>\n<p>Wszystko pi\u0119knie dzia\u0142a, adresy i\u015bcie jedwabiste, ale masz formularz wyszukiwarki na stronie, szukasz, a tu w adresie paskudne <em>?query=fraza<\/em>. My\u015blisz, co jest grane, przecie\u017c przepisywanie dzia\u0142a ok, szukanie niekoniecznie, ale na Wrzucie tak robi\u0105 i jako\u015b dzia\u0142a. Ok, wy\u0142\u0105cz JavaScript i spr\u00f3buj jeszcze raz. Te\u017c dzia\u0142a? No widzisz.<\/p>\n<p>Zacznijmy od naprawienia zwyk\u0142ego formularza. Masz flag\u0119 <em>[QSA]<\/em> na ko\u0144cu regu\u0142ek? ;]<\/p>\n<p><code lang=\"apache\">RewriteRule WZORZEC ZAMIENNIK [QSA]<\/code><\/p>\n<p>Bez tej flagi nie zadzia\u0142a. Je\u015bli chodzi o przyjazne URL a&#8217;la wrzuta, to bez pomocy JS nie da si\u0119 osi\u0105gn\u0105\u0107 czego\u015b takiego, aby zapytanie by\u0142o przekierowywane bezpo\u015brednio do URL <em>\/szukaj\/fraza<\/em>. Je\u015bli jednak jeste\u015b uparty(a), \u017ceby skorzysta\u0107 z takiego u\u0142atwiacza, to skorzystaj z mniej wi\u0119cej takiego kodu:<\/p>\n<p><code lang=\"html\"><\/p>\n<form id=\"search\" method=\"get\" action=\"\/szukaj\">\n<p><input type=\"text\" name=\"query\" \/><\/p>\n<p><input type=\"submit\" value=\"szukaj\" \/><\/p>\n<\/form>\n<p><script type=\"text\/javascript\">\nwindow.onload = function(){\n    var form = document.getElementById('search');<\/p>\n<p>    form.onsubmit = function(){\n        window.location.replace('\/szukaj\/'+encodeURIComponent(form.getElementsByName('query')[0].value));\n        return false;\n    }    <\/p>\n<p>}\n<\/script><\/code><\/p>\n<p>Ale to nie zmienia faktu, \u017ce nie wszyscy maj\u0105 w\u0142\u0105czony JS. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_tongue.png\" alt=\":P\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/><\/p>\n<h3 id=\"t9\">epilog<\/h3>\n<p>Nad notk\u0105 sp\u0119dzi\u0142em par\u0119 \u0142adnych godzin, a \u017ce ostatnio pewne osoby s\u0105 \u0142ase na tre\u015b\u0107 dla cel\u00f3w SEO, jestem zmuszony zrezygnowa\u0107 z Creative-Commons przynajmniej dla tej notki. Chcesz opublikowa\u0107 &#8211; <a href=\"http:\/\/eriz.pcinside.pl\/kontakt\">skontaktuj si\u0119 ze mn\u0105<\/a>, nie b\u0119d\u0119 tolerowa\u0107 samowolki.<\/p>\n<p>Nie wszystko zd\u0105\u017cy\u0142em zweryfikowa\u0107 &#8211; komentarze i uwagi s\u0105 &#8211; jak zwykle &#8211; mile widziane. <img src=\"https:\/\/eriz.pcinside.pl\/weblog\/wp-includes\/images\/smilies\/e_smile.png\" alt=\":)\" class=\"wp-smiley\" style=\"height: 1em; max-height: 1em;\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>mod_rewrite, rewriting, przyjazne URL-e\/adresy, maskowanie, przepisywanie, nazw jest sporo. Zreszt\u0105, najpopularniejsza pochodzi od swojego protoplasty &#8211; czyli mod_rewrite powsta\u0142ego pod skrzyd\u0142ami Apache&#8217;a jako modu\u0142. Teraz w\u0142a\u015bciwie standard, je\u015bli chodzi o nowoczesne strony www &#8211; nie tylko ze wzgl\u0119du na wygl\u0105d, ale i (jak ptaszki \u0107wierkaj\u0105, cho\u0107 jest to w\u0105tpliwe wobec oficjalnych \u017ar\u00f3de\u0142) SEO. Jak zwa\u0142, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15,3,11],"tags":[31,37,32,30,112,105,74],"_links":{"self":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts\/208"}],"collection":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/comments?post=208"}],"version-history":[{"count":0,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/posts\/208\/revisions"}],"wp:attachment":[{"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/media?parent=208"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/categories?post=208"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eriz.pcinside.pl\/weblog\/wp-json\/wp\/v2\/tags?post=208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}