The Content-MD5 HTTP Header was recently put in place on WordPress.org for WordPress releases to allow easier MD5 checksum verification of the download without requiring an extra HTTP request to grab an MD5 file. WordPress will soon start validating the MD5 checksum when downloading files based on this header.
I had recently implemented some code in a WordPress plugin, for downloading some source javascript files and then verifying them by requesting an MD5 file, and comparing the MD5 of the downloaded source file to that of what we expected. I had wanted to use the Content-MD5 header, but as I am running nginx, and nginx not supporting it, I decided for the time being to leave it, making the extra request.
I knew the solution to adding it was in using the nginx embedded perl module, and to make sure that I didn’t block the delivery of the content, that pulling an MD5 hex hash out of an MD5 file that lived along side the original file would be best.
I ended up writing two versions, one that would only pull the MD5 out of an MD5 file, and one that would fall back to compute the MD5 hex hash on the fly if an MD5 file didn’t exist.
Here is the finished product:
# nginx Embedded Perl module for adding a Content-MD5 HTTP header
#
# This perl module, will output an MD5 of a requested file using the
# Content-MD5 HTTP header, by pulling the hex hash from a file of the
# same name with .md5 appended to the end, if it exists.
#
# Author: Matt Martz <matt@sivel.net>
# Link: https://gist.github.com/1870822#file_content_md5_req_dot_md5.pm
# License: http://www.nginx.org/LICENSE
package ContentMD5;
use nginx;
sub handler {
my $r = shift;
my $filename = $r->filename;
return DECLINED unless ( -f $filename && -f "$filename.md5" );
my $content_length = -s $filename;
open( MD5FILE, "$filename.md5" ) or return DECLINED;
my $md5 = <MD5FILE>;
close( MD5FILE );
$md5 =~ s/^\s+//;
$md5 =~ s/\s+$//;
$md5 =~ s/\ .*//;
$r->header_out( "Content-MD5", $md5 ) unless ! $md5;
return DECLINED;
}
1;
__END__
# nginx Embedded Perl module for adding a Content-MD5 HTTP header
#
# This perl module, will output an MD5 of a requested file using the
# Content-MD5 HTTP header, by either pulling it from a file of the
# same name with .md5 appended to the end, if it exists, or will
# calculate the MD5 hex hash on the fly
#
# Author: Matt Martz <matt@sivel.net>
# Link: https://gist.github.com/1870822#file_content_md5.pm
# License: http://www.nginx.org/LICENSE
package ContentMD5;
use nginx;
use Digest::MD5;
sub handler {
my $r = shift;
my $filename = $r->filename;
return DECLINED unless -f $filename;
my $content_length = -s $filename;
my $md5;
if ( -f "$filename.md5" ) {
open( MD5FILE, "$filename.md5" ) or return DECLINED;
$md5 = <MD5FILE>;
close( MD5FILE );
$md5 =~ s/^\s+//;
$md5 =~ s/\s+$//;
$md5 =~ s/\ .*//;
} else {
open( FILE, $filename ) or return DECLINED;
my $ctx = Digest::MD5->new;
$ctx->addfile( *FILE );
$md5 = $ctx->hexdigest;
close( FILE );
}
$r->header_out( "Content-MD5", $md5 ) unless ! $md5;
return DECLINED;
}
1;
__END__
# nginx sample configuration for adding a Content-MD5 HTTP header
# using the Embedded Perl module
#
# Author: Matt Martz <matt@sivel.net>
# Link: https://gist.github.com/1870822#file_nginx.conf
http {
perl_modules perl/lib;
perl_require ContentMD5.pm;
server {
location / {
root /var/www;
index index.html
}
location /download {
perl ContentMD5::handler;
}
}
}
Here is an example of the resultant HTTP Headers:
$ curl -I HTTP/1.1 200 OK Server: nginx/1.0.5 Date: Mon, 20 Feb 2012 20:19:04 GMT Content-Type: application/octet-stream Content-Length: 134217728 Last-Modified: Mon, 20 Feb 2012 20:18:25 GMT Connection: keep-alive Content-MD5: fde9e0818281836e4fc0edfede2b8762 Accept-Ranges: bytes
$ curl -i HTTP/1.1 200 OK Server: nginx/1.0.5 Date: Mon, 20 Feb 2012 20:28:57 GMT Content-Type: application/octet-stream Content-Length: 42 Last-Modified: Mon, 20 Feb 2012 20:28:32 GMT Connection: keep-alive Content-MD5: 017d95ac06d4200bbcb2a5682e873fd7 Accept-Ranges: bytes fde9e0818281836e4fc0edfede2b8762 128.bin
$ curl -I HTTP/1.1 200 OK Server: nginx/1.0.5 Date: Mon, 20 Feb 2012 20:29:27 GMT Content-Type: application/octet-stream Content-Length: 134217728 Last-Modified: Mon, 20 Feb 2012 20:28:20 GMT Connection: keep-alive Content-MD5: fde9e0818281836e4fc0edfede2b8762 Accept-Ranges: bytes
$ curl -I HTTP/1.1 404 Not Found Server: nginx/1.0.5 Date: Mon, 20 Feb 2012 20:29:36 GMT Content-Type: text/html Content-Length: 168 Connection: keep-alive
Learn more about how Rackspace can help you with hosting your WordPress site.