title: Regex negative lookbehind and lookahead: equivalence and performance
body: <p>I need a regex that will select only those URL strings NOT ending with specific extensions like .png or .css.</p>
  
  <p>I tested the following:</p>
  
  <p>1) this one using negative lookbehind:</p>
  
  <pre><code>(?&lt;!\.png|\.css)$
  </code></pre>
  
  <p><a href="https://regex101.com/r/tW4fO5/1" rel="noreferrer">https://regex101.com/r/tW4fO5/1</a></p>
  
  <p>2) this other one using negative lookahead:</p>
  
  <pre><code>^(?!.*[.]png|.*[.]css$).*$
  </code></pre>
  
  <p><a href="https://regex101.com/r/qZ7vA4/1" rel="noreferrer">https://regex101.com/r/qZ7vA4/1</a></p>
  
  <p>Both seems to work fine, but #1 (negative lookbehind) is said to be processed in 436 steps (see the link), while #2 (negative lookahead) is said to be processed in 173 steps.</p>
  
  <p>So my question is: what does that mean? Is it going to have an impact on performances?</p>
  
  <p>And lastly, are the two regex really functionally equivalent?</p>
  
  <p><strong>EDIT: SOLUTION SUMMARY</strong></p>
  
  <p>Just to wrap things up, considering the full list of string-endings to be excluded via the regex (a typical scenario would be a web server setup where static resources are served by apache while dynamic stuff is served by a different engine - in my case: php-fpm).</p>
  
  <p>Two options are possible with PCRE regex:</p>
  
  <p>1) <strong>negative lookbehind</strong></p>
  
  <p><code>$(?&lt;!\.(?:ico|gif|jpg|png|css|rss|xml|htm|pdf|zip|txt|ttf)$|(?:js|gz)$|(?:html|woff)$)</code></p>
  
  <p><a href="https://regex101.com/r/eU9fI6/1" rel="noreferrer">https://regex101.com/r/eU9fI6/1</a></p>
  
  <p>Notice that I used several OR-ed lookbehinds because the negative lookbehind requires a fixed-width pattern (ie: you cannot mix patterns of different lengths). This makes this options sligthly more complex to write. Moreover this lowers its performance in my opinion.</p>
  
  <p>2) <strong>negative lookahead</strong></p>
  
  <p><code>^(?!.*[.](?:js|ico|gif|jpg|png|css|rss|xml|htm|html|pdf|zip|gz|txt|ttf|woff)$).*$</code></p>
  
  <p><a href="https://regex101.com/r/dP7uD9/1" rel="noreferrer">https://regex101.com/r/dP7uD9/1</a></p>
  
  <p>The lookahead is slightly faster than the lookbehind. This is a test result from making 1 million iterations:</p>
  
  <p>time lookbehind = 18.469825983047 secs<br>
  time lookahead = 14.316685199738 secs</p>
  
  <p>If I had not the issue of the variable lenght patterns, I would pick the lookbehind since it looks more compact. Either one is good anyway. At the end, I went with the lookahead:</p>
  
  <pre><code>&lt;LocationMatch "^(?!.*[.](?:js|ico|gif|jpg|png|css|rss|xml|htm|html|pdf|zip|gz|txt|ttf|woff)$).*$"&gt;
      SetHandler "proxy:unix:/var/run/php5-fpm.sock|fcgi://www/srv/www/gioplet/web/public/index.php"
  &lt;/LocationMatch&gt;
  </code></pre>