--- layout: post title: '为 WordPress 配置 cookie-free domain 和 SSL' comments: true published: false date: '2013-05-13 00:15:54' link: http://opoo.org/cookie-free-domain-and-ssl-for-wordpress/ post_id: 323 url: '/cookie-free-domain-and-ssl-for-wordpress/' excerpt: "<p>最近在配置 WordPress 的过程中,笔者希望可以做到以下几点:<ol> <li>使用 cookie-free 域来访问静态文件,主要是 css,js,图片等,大多位于 wp-content 目录。</li> <li>给网站配置 SSL 证书,使得网站既可以通过 HTTP 访问,也可以通过 HTTPS 访问。</li> <li>在通过 HTTPS 访问网站时,不应该出现网页中既包含安全内容也包含不安全内容(及混合内容)的警告,即此时所有引用资源都应该是 HTTPS 协议访问的。</li> <li>不希望通过目录级配置文件 .htaccess 文件来实现 WordPress 的固定链接,因为启用 .htaccess 会降低 Apache 的性能。</li></ol>下面具体说明如何达到这些目标。" categories: [website] tags: [cookie-free, Apache, SSL, WordPress] description: "本文重要介绍:为 WordPress 配置 cookie-free 域;为 WordPress 配置 SSL 证书;修改相应的配置文件使得 WordPress 可以同时通过 HTTP 和 HTTPS 访问,并且在 HTTPS 访问时不出现混合内容的警告。" keywords: "WordPress, SSL, cookie-free domains, Apache" --- <p>最近在配置 WordPress 的过程中,笔者希望可以做到以下几点: <ol> <li>使用 cookie-free 域来访问静态文件,主要是 css,js,图片等,大多位于 wp-content 目录。</li> <li>给网站配置 SSL 证书,使得网站既可以通过 HTTP 访问,也可以通过 HTTPS 访问。</li> <li>在通过 HTTPS 访问网站时,不应该出现网页中既包含安全内容也包含不安全内容(及混合内容)的警告,即此时所有引用资源都应该是 HTTPS 协议访问的。</li> <li>不希望通过目录级配置文件 .htaccess 文件来实现 WordPress 的固定链接,因为启用 .htaccess 会降低 Apache 的性能。</li> </ol> <p>下面具体说明如何达到这些目标。 <h1>一、cookie-free 域的选择</h1> <p><a title="关于 cookie-free domains" href="/cookie-free-domains/">有关 cookie-free domains,请参考上一篇文章</a>。 <p>要达到第一点要求,必须在主域之外,使用第二个域作为 cookie-free 域。 <p>要达到第二点和第三点要求,该 cookie-free 域:a)必须能通过 HTTPS 访问;b)不能直接修改 WordPress 源文件中静态资源为 http://cookie-free.domain/...,写死之后在 HTTPS 访问时会出现混合内容警告。 <!--more--> <p>关于a):笔者选择 opoo.org 作为主域,www.opoo.org 作为 cookie-free 域,因为笔者申请的 StartSSL 的证书只支持这两个域,如果读者有其它支持 HTTPS 的域,也可以选择来做 cookie-free 的域。 <p>关于b):笔者主要对 wp-content 目录下的内容做 cookie-free 处理。修改 <code>wp-config.php</code> 文件,添加以下一行 <pre class="brush:php">define('WP_CONTENT_URL', 'http://www.opoo.org/wp-content');</pre> <strong>好处:</strong> <ol> <li>在通过 HTTPS 协议访问主域时,WordPress 会自动将 WP_CONTENT_URL 也变成 https 协议,即变成 <code><strong>https</strong>://www.opoo.org/wp-content</code>;</li> <li>WordPress 大部分的静态内容都在这个目录下,包括文章上传的图片、主题、插件等。这样,一个 WordPress 的大部分静态资源都已经 cookie-free 了。</li> </ol> <strong>坏处:</strong> <p>可能会导致部分主题或插件工作不正常。 <p>主题、插件目录中除了静态文件,还有部分 php 文件。这些 php 文件在调用时 url 也变成了 http://www.opoo.org/wp-content/.../x.php 的形式,如果 php 文件运行时需要主域的 cookie,就会出错。 <p>笔者目前发现语法高亮插件 Syntax Highlighter ComPress 就有这个问题,它在后台管理时通过 cookie-free 域访问了一个 php 文件,由于拿不到 Cookie 信息,该 php 直接返回一个错误信息。 <p>解决这个问题的办法是通过 Rewrite 规则重写,将所有的 <strong>php 文件</strong>或者 <strong>非 wp-content 目录</strong>下的文件的访问都转发回主域。 <pre class="brush:plain"># cookie-free 域下所有的 php 访问转发回主域 RewriteCond %{HTTP_HOST} ^www.opoo.org${'$'} [NC] RewriteCond %{REQUEST_URI} (.*).php${'$'} [NC] RewriteRule ^(.*)${'$'} http://opoo.org${'$'}1 [L,R=301] # 除了 wp-content 开头的 url,cookie-free 域下其它访问转发回主域 RewriteCond %{HTTP_HOST} ^www.opoo.org${'$'} [NC] RewriteCond %{REQUEST_URI} !^/wp-content/ RewriteRule ^(.*)${'$'} http://opoo.org${'$'}1 [L,R=301]</pre> <p>另外也需要在 ssl.conf 中配置,将以上内容中的 http 换成 https 即可。 <p>如果读者是超级完美主义者,想将 wp-includes 目录下的静态文件也 cookie-free,那么就要修改 WordPress 源文件。建议修改时判断一下,以保证能同时在 HTTP 和 HTTPS 下访问。 <pre class="brush:php">${'$'}url = (is_ssl() ? "https" : "http") . "://www.opoo.org/wp-includes/.../css.css"</pre> 最后,将已有文章中的图片的 url 替换成 cookie-free domain,执行以下 SQL 语句 <pre class="brush:sql">update wp_posts set post_content=replace(post_content, 'http://opoo.org/wp-content/uploads', 'http://www.opoo.org/wp-content/uploads') where post_content like '%http://opoo.org/wp-content/uploads%';</pre> <p>这样修改后文章中的所有文章都指向了 http 开头的 cookie-free 域,在 HTTPS 访问主域会出现混合内容警告信息,可建一个插件解决这个问题。插件主要代码如下: <pre class="brush:php">function my_content_manipulator(${'$'}content){ if( is_ssl() ){ ${'$'}content = str_replace('http://www.opoo.org/wp-content/uploads', 'https://www.opoo.org/wp-content/uploads', ${'$'}content); } return ${'$'}content; } add_filter('the_content', 'my_content_manipulator');</pre> <p>通过 HTTPS 访问时,文章在显示之前会被插件替换其中图片 url 的协议。 <h1>二、配置 SSL</h1> <h2>1. 申请 SSL 证书</h2> <p>要实现通过 HTTPS 访问网站,需要购买一个用于 WEB 服务器的 SSL 证书,也可在 StartSSL 免费申请。 <p>如果仅用于测试,也可以通过 openssl 之类的软件生成一个自签名的证书。自签名的证书在访问时会显示网站不受信任,在谷歌浏览器地址栏里有显示一个叉,别的浏览器也会有相应提示。 <p>如果用于正式的生产环境,非常不建议使用自签名证书,不要像某道部的网站一样瞎搞。 <h2>2. 安装证书</h2> 以 Apache 服务器和从 StartSSL 申请的免费 SSL 证书为例来说明如何安装和配置。 <p>在 StartSSL 证书申请过程中,先是保存的密匙文件 <code>ssl.key</code>,后来又保存了颁发的证书文件 <code>ssl.crt</code>,在 StartSSL 的安装指引里要求下载文件 <code>ca.pem</code> 和 <code>sub.class1.server.ca.pem</code>,将这 4 个文件上传到服务器上(如目录 /etc/pki/ssl/),然后修改 Apache 的配置文件(也可以在 conf.d 目录下新建文件 ssl.conf),增加以下内容: <pre class="brush:plain">LoadModule ssl_module modules/mod_ssl.so Listen 443 <VirtualHost _default_:443> DocumentRoot /var/www/wordpress_root ErrorLog logs/error_log TransferLog logs/access_log SSLEngine on SSLProtocol all -SSLv2 SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM SSLCertificateFile /etc/pki/ssl/ssl.crt SSLCertificateKeyFile /etc/pki/ssl/ssl.key SSLCertificateChainFile /etc/pki/ssl/sub.class1.server.ca.pem SSLCACertificateFile /etc/pki/ssl/ca.pem CustomLog logs/ssl_request_log \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" </VirtualHost></pre> <p>这是为 Apache 配置 SSL 最核心的部分。 <p>必须为 Apache 安装 mod_ssl 模块。CentOS 通过 <code>yum install mod_ssl</code> 即可安装该模块,并在 <code>/etc/httpd/conf.d/</code> 下生成配置文件 ssl.conf,修改其内容即可。 <p>在 StartSSL 创建的私匙是加密的,启动 Apache 服务器时,可能提示输入的密码,输入申请过程中填写的密码即可。 <p>如果要去除这个提示,可对私匙进行解密,执行以下命令 <pre class="brush:shell">#openssl rsa -in ssl.key -out ssl.key #chmod 400 ssl.key</pre> <h2>3. HTTPS 访问时的混合内容警告及其它</h2> a) 混合内容警告 <p>如果网站是通过 HTTPS 访问的,而网页中引用了其它 HTTP 协议访问的资源时,就会出现混合内容的警告,在谷歌浏览器下显示成这样: <a href="/wp-content/uploads/2013/05/ssl.mixed_.content.png"><img class="alignnone size-full wp-image-335" alt="ssl.mixed.content" src="/wp-content/uploads/2013/05/ssl.mixed_.content.png" width="135" height="23" /></a> 点开小三角可以看见一段描述“但是,此网页中包含其他不安全的资源”。而引用资源也全部是 https 时,则显示成这样:<a href="/wp-content/uploads/2013/05/ssl.good_.png"><img class="alignnone size-full wp-image-336" alt="ssl.good" src="/wp-content/uploads/2013/05/ssl.good_.png" width="128" height="23" /></a> <p>混合内容在 IE 下会导致浏览器直接弹出一个警告框“本页不但包含安全的内容,也包含不安全的内容。是否显示不安全的内容?”,必须选择是才能访问。 <p>b) 如果 HTTPS 页面引用了 HTTP 协议访问的 JavaScript 脚本,那么该脚本会被屏蔽,直接不执行(至少在谷歌浏览器里的表现是这样的)。打开谷歌浏览器的控制台 console 看以看到警告信息,以及被 blocked 的脚本。 <p>基于以上两点,应该尽量确保页面中所有引用资源和页面本身使用同一种访问协议(http 或者 https),起码保证 HTTPS 协议下时应该一致。 <p>HTTP 协议下引用 HTTPS 资源是不会出现警告的,直接将 cookie-free 域下所有资源都改为 https 访问倒是相当省事的做法。但 HTTPS 是一种加密传输的协议,理论上会降低访问速度。所在既然主域使用 HTTP 协议了,cookie-free 域也尽量使用 HTTP 协议。 <p>(完美主义强迫症患者 - -! ) <h1>三、不使用 .htaccess 文件</h1> <p>Apache 目录级的配置文件 .htaccess 会降低 Apache 的性能,尽管很小。 <p>Apache 配置 <code>AllowOverride None</code> 时会忽略目录级的配置文件。 <p>WordPress 使用固定链接时,需要 htaccess 的支持,如果不启用 .htaccess, 可以将配置写进 Apache 主配置文件。如下: <pre class="brush:plain"><Directory "/var/www/wordpress_root"> <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule> <Files "wp-config.php"> Order allow,deny Deny from all </Files> </Directory></pre> <h1>四、总结</h1> 以下几个配置综合了上文中所讲的各个需求 <h2>1. Apache 配置 wordpress.conf</h2> <pre class="brush:plain">DocumentRoot /var/www/wordpress_root ServerName opoo.org ServerAlias www.opoo.org RewriteEngine On RewriteCond %{HTTP_HOST} ^www.opoo.org${'$'} [NC] RewriteCond %{REQUEST_URI} (.*).php${'$'} [NC] RewriteRule ^(.*)${'$'} http://opoo.org${'$'}1 [L,R=301] RewriteCond %{HTTP_HOST} ^www.opoo.org${'$'} [NC] RewriteCond %{REQUEST_URI} !^/wp-content/ [NC] RewriteRule ^(.*)${'$'} http://opoo.org${'$'}1 [L,R=301] <Directory "/var/www/wordpress_root"> Options -Indexes FollowSymLinks AllowOverride None Order allow,deny Allow from all <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] RewriteRule ^wp-admin/includes/ - [F,L] RewriteRule !^wp-includes/ - [S=3] RewriteRule ^wp-includes/[^/]+\.php${'$'} - [F,L] RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L] RewriteRule ^wp-includes/theme-compat/ - [F,L] </IfModule> <Files "wp-config.php"> Order allow,deny Deny from all </Files> </Directory></pre> <h2>2. Apache 配置 wordpress_ssl.conf</h2> <pre class="brush:plain">LoadModule ssl_module modules/mod_ssl.so Listen 443 <VirtualHost _default_:443> DocumentRoot /var/www/wordpress_root ServerName opoo.org ServerAlias www.opoo.org ErrorLog /usr/local/apache/logs/ssl-error_log TransferLog /usr/local/apache/logs/ssl-transfer_log CustomLog /usr/local/apache/logs/ssl-access_log \ "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" SSLEngine on SSLProtocol all -SSLv2 SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM SSLCertificateFile /etc/pki/ssl/ssl.crt SSLCertificateKeyFile /etc/pki/ssl/ssl.key SSLCertificateChainFile /etc/pki/ssl/sub.class1.server.ca.pem SSLCACertificateFile /etc/pki/ssl/ca.pem SetEnvIf User-Agent ".*MSIE.*" \ nokeepalive ssl-unclean-shutdown \ downgrade-1.0 force-response-1.0 RewriteEngine On RewriteCond %{HTTP_HOST} ^www.opoo.org${'$'} [NC] RewriteCond %{REQUEST_URI} (.*).php${'$'} [NC] RewriteRule ^(.*)${'$'} https://opoo.org${'$'}1 [L,R=301] RewriteCond %{HTTP_HOST} ^www.opoo.org${'$'} [NC] RewriteCond %{REQUEST_URI} !^/wp-content/ [NC] RewriteRule ^(.*)${'$'} https://opoo.org${'$'}1 [L,R=301] <Directory "/var/www/wordpress_root"> Options -Indexes FollowSymLinks AllowOverride None Order allow,deny Allow from all <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] RewriteRule ^wp-admin/includes/ - [F,L] RewriteRule !^wp-includes/ - [S=3] RewriteRule ^wp-includes/[^/]+\.php${'$'} - [F,L] RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L] RewriteRule ^wp-includes/theme-compat/ - [F,L] </IfModule> <Files "wp-config.php"> Order allow,deny Deny from all </Files> </Directory> </VirtualHost></pre> <h2>3. WordPress 配置 wp-config.php 增加</h2> <pre class="brush:php">/** * 在管理页面全部使用 HTTPS */ define('FORCE_SSL_ADMIN', true); /** * For cookie-free */ define('WP_CONTENT_URL', 'http://www.opoo.org/wp-content');</pre> <h2>4. 自定义 WordPress 插件用于过滤文章内容</h2> <pre class="brush:php">function my_content_manipulator(${'$'}content){ if( is_ssl() ){ ${'$'}content = str_replace('http://www.opoo.org/wp-content/uploads', 'https://www.opoo.org/wp-content/uploads', ${'$'}content); } return ${'$'}content; } add_filter('the_content', 'my_content_manipulator');</pre>