diff --git a/Tests/LibWeb/Text/expected/XHR/XMLHttpRequest-responseURL.txt b/Tests/LibWeb/Text/expected/XHR/XMLHttpRequest-responseURL.txt
new file mode 100644
index 00000000000..cdecef5659b
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/XHR/XMLHttpRequest-responseURL.txt
@@ -0,0 +1,2 @@
+responseURL before: ''
+responseURL after: 'data:text/html,hello'
diff --git a/Tests/LibWeb/Text/input/XHR/XMLHttpRequest-responseURL.html b/Tests/LibWeb/Text/input/XHR/XMLHttpRequest-responseURL.html
new file mode 100644
index 00000000000..19a2c72b4e1
--- /dev/null
+++ b/Tests/LibWeb/Text/input/XHR/XMLHttpRequest-responseURL.html
@@ -0,0 +1,15 @@
+
+
diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp
index 426246e996e..3d8598fe3f7 100644
--- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp
+++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp
@@ -1265,4 +1265,16 @@ JS::ThrowCompletionOr XMLHttpRequest::request_error_steps(FlyString const&
return {};
}
+// https://xhr.spec.whatwg.org/#the-responseurl-attribute
+String XMLHttpRequest::response_url()
+{
+ // The responseURL getter steps are to return the empty string if this’s response’s URL is null;
+ // otherwise its serialization with the exclude fragment flag set.
+ if (!m_response->url().has_value())
+ return String {};
+
+ auto serialized = m_response->url().value().serialize(URL::ExcludeFragment::Yes);
+ return String::from_utf8_without_validation(serialized.bytes());
+}
+
}
diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h
index d5b436d8dac..caf744d3426 100644
--- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h
+++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h
@@ -52,6 +52,7 @@ public:
WebIDL::ExceptionOr> response_xml();
WebIDL::ExceptionOr response();
Bindings::XMLHttpRequestResponseType response_type() const { return m_response_type; }
+ String response_url();
WebIDL::ExceptionOr open(String const& method, String const& url);
WebIDL::ExceptionOr open(String const& method, String const& url, bool async, Optional const& username = Optional {}, Optional const& password = Optional {});
diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.idl b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.idl
index e6223ddb184..bb9dae32300 100644
--- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.idl
+++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.idl
@@ -41,7 +41,7 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget {
undefined abort();
// response
- // FIXME: readonly attribute USVString responseURL;
+ readonly attribute USVString responseURL;
readonly attribute unsigned short status;
readonly attribute ByteString statusText;
ByteString? getResponseHeader(ByteString name);