diff --git a/core/modules/openid/openid.inc b/core/modules/openid/openid.inc
index 6945f34..f19f273 100644
--- a/core/modules/openid/openid.inc
+++ b/core/modules/openid/openid.inc
@@ -371,24 +371,32 @@ function _openid_nonce() {
  * Pull the href attribute out of an html link element.
  */
 function _openid_link_href($rel, $html) {
-  $rel = preg_quote($rel);
-  preg_match('|<link\s+rel=["\'](.*)' . $rel . '(.*)["\'](.*)/?>|iUs', $html, $matches);
-  if (isset($matches[3])) {
-    preg_match('|href=["\']([^"]+)["\']|iU', $matches[3], $href);
-    return trim($href[1]);
+  $html_dom = @DOMDocument::loadHTML($html);
+  if ($html_dom) {
+    $html_element = simplexml_import_dom($html_dom);
+    foreach ($html_element->head->link as $link) {
+      // The rel attribute contains a space-separated list of case-insensitive
+      // link types.
+      if (preg_match('/(\s|^)' . $rel . '(\s|$)/i', $link['rel'])) {
+        return trim($link['href']);
+      }
+    }
   }
   return FALSE;
 }
 
 /**
- * Pull the http-equiv attribute out of an html meta element
+ * Pull the content attribute out of an X-XRDS-Location meta http-equiv element.
  */
-function _openid_meta_httpequiv($equiv, $html) {
-  preg_match('|<meta\s+http-equiv=["\']' . $equiv . '["\'](.*)/?>|iUs', $html, $matches);
-  if (isset($matches[1])) {
-    preg_match('|content=["\']([^"]+)["\']|iUs', $matches[1], $content);
-    if (isset($content[1])) {
-      return $content[1];
+function _openid_meta_httpequiv($html) {
+  $html_dom = @DOMDocument::loadHTML($html);
+  if ($html_dom) {
+    $html_element = simplexml_import_dom($html_dom);
+    foreach ($html_element->head->meta as $meta) {
+      // The http-equiv attribute is case-insensitive.
+      if (strtolower(trim($meta['http-equiv'])) == 'x-xrds-location') {
+        return trim($meta['content']);
+      }
     }
   }
   return FALSE;
diff --git a/core/modules/openid/openid.module b/core/modules/openid/openid.module
index ed02891..f2aa766 100644
--- a/core/modules/openid/openid.module
+++ b/core/modules/openid/openid.module
@@ -517,7 +517,7 @@ function _openid_xrds_discovery($claimed_id) {
         }
         else {
           // Look for meta http-equiv link in HTML head
-          $xrds_url = _openid_meta_httpequiv('X-XRDS-Location', $result->data);
+          $xrds_url = _openid_meta_httpequiv($result->data);
         }
         if (!empty($xrds_url)) {
           $headers = array('Accept' => 'application/xrds+xml');
diff --git a/core/modules/openid/tests/openid_test.module b/core/modules/openid/tests/openid_test.module
index 629dcd3..3cb2dcc 100644
--- a/core/modules/openid/tests/openid_test.module
+++ b/core/modules/openid/tests/openid_test.module
@@ -181,7 +181,7 @@ function openid_test_yadis_http_equiv() {
   $element = array(
     '#tag' => 'meta',
     '#attributes' => array(
-      'http-equiv' => 'X-XRDS-Location',
+      'http-equiv' => "X-XRDS-Location\n",
       'content' => url('openid-test/yadis/xrds', array('absolute' => TRUE)),
     ),
   );
@@ -202,7 +202,8 @@ function openid_test_html_openid1() {
  * Menu callback; regular HTML page with OpenID 2.0 <link> element.
  */
 function openid_test_html_openid2() {
-  drupal_add_html_head_link(array('rel' => 'openid2.provider', 'href' => url('openid-test/endpoint', array('absolute' => TRUE))));
+  // Use unusual values in order to test proper parsing of HTML attributes.
+  drupal_add_html_head_link(array('rel' => "foo\nopenid2.PROVIDER\tbar", 'href' => "\n" . url('openid-test/endpoint', array('absolute' => TRUE))));
   drupal_add_html_head_link(array('rel' => 'openid2.local_id', 'href' => 'http://example.com/html-openid2'));
   return t('This page includes a &lt;link rel=...&gt; element containing the URL of an OpenID Provider Endpoint.');
 }
