Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return empty response from Apache

Tags:

cors

apache

I'm using Apache to return CORS responses to speed-up the requests (previously I handled this in application code but this was too slow). In my VirtualHost, I have the following Apache code:

SetEnvIfNoCase Access-Control-Request-Method "(GET|POST|PUT|DELETE|OPTIONS)" IsPreflight=1
SetEnvIfNoCase Origin "http(s)?://(myorigin.com)$" AccessControlAllowOrigin=$0$1

Header always set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" env=IsPreflight
Header always set Access-Control-Allow-Headers "Content-Type, Authorization, Accept" env=IsPreflight
Header always set Access-Control-Max-Age "7200" env=IsPreflight

RewriteEngine On   
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteCond %{ENV:IsPreflight} 1
RewriteRule ^(.*)$ $1 [R=200,L]

This actually works surprinsigly good. It detects if the request is a preflight request, and if it is, it sends the appropriate headers. There is only one catch: the preflight request returns 200 (so the browser sends the normal request), but the body is a 200 ERROR (haha):

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>200 OK</title>
</head><body>
<h1>OK</h1>
<p>The server encountered an internal error or
misconfiguration and was unable to complete
your request.</p>
<p>Please contact the server administrator at 
 [email protected] to inform them of the time this error occurred,
 and the actions you performed just before this error.</p>
<p>More information about this error may be available
in the server error log.</p>
</body></html>

While this works correctly, I'd like to remove this ugly error from the preflight body, but I didn't find any way to tell Apache to actually returns an empty body.

Thanks!

like image 716
Michael Gallego Avatar asked Dec 30 '14 10:12

Michael Gallego


2 Answers

Response with 204 Code (No Content) for OPTIONS requests.

For example:

RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=204,L]
like image 68
Sergey Kandyla Avatar answered Oct 28 '22 16:10

Sergey Kandyla


A 204 should be sufficient for CORS restriction. In other cases, however, an empty response might be desired with different status codes.

TL;DR

Be warned: ErrorDocument with 2xx and 3xx is undocumented behavior.

To send a response with an empty body, combine RewriteRule with ErrorDocument.

# for 2xx, 4xx, 5xx, with or without flag [L]
RewriteRule ^ - [R=code,L]

# for 3xx
RewriteRule pattern substitution [R=code,L]

# for apache 2.4.13 and later
ErrorDocument code %{unescape:%00}

# for older versions,
# this can't be done without an empty file,
# the closest you can get using only the configuration is
ErrorDocument code " " 

# if you're willing to use an empty file
ErrorDocument code /path/to/empty/file

About 204 No Content

RewriteRule with 204 No Content works as expected except that major browsers handle it differently from other status codes such as 200 and 301 with empty content.

As specified in MDN: 204 No Content,

The HTTP 204 No Content success status response code indicates that the request has succeeded, but that the client doesn't need to go away from its current page.

If you visit a URL and it returns 204, Firefox and Chromium will act as if nothing has happened, leaving the content of the page and URL in the navigation bar unchanged.

This may be desired or not.

But if you want the same effect as a 200 response with empty content and care about Firefox/Chromium/etc, or want to use other HTTP status codes, then RewriteRule with 204 is not the solution.

Custom response body with ErrorDocument

The closest way I can find to return an empty response is to return a custom response body using ErrorDocument, and set the desired body content.

Despite Apache's Custom Error Responses documentation that states:

Customized error responses can be defined for any HTTP status code designated as an error condition - that is, any 4xx or 5xx status.

It turns out customized responses can be specified by ErrorDocument for 2xx and 3xx status codes.

And we can use RewriteRule's [R] flag to enter such status for ErrorDocument to take effect.

However, this is not the end of the story, as the configuration below,

ErrorDocument 200 ""

will raise this error message,

ErrorDocument takes two arguments, Change responses for HTTP errors

It seems the quoted "empty string" doesn't make way to the ErrorDocument directive, and Apache parses this configuration line as a directive with a single argument.

I can't find a satisfying explanation for this or any hint for an empty string as an argument after reading Apache's Syntax of the Configuration Files, and looking for "space" and "quote" in the documentation. It only mentions quotes in the case of arguments containing spaces.

Digging into ErrorDocument

As Apache's Custom Error Responses says,

The syntax of the ErrorDocument directive is:

ErrorDocument <3-digit-code> <action>

where the action will be treated as:

  1. A local URL to redirect to (if the action begins with a "/").
  2. An external URL to redirect to (if the action is a valid URL).
  3. Text to be displayed (if none of the above). The text must be wrapped in quotes (") if it consists of more than one word.

Also noted in Apache's ErrorDocument Directive,

From 2.4.13, expression syntax can be used inside the directive to produce dynamic strings and URLs.

We should be able to use expressions in ErrorDocument from 2.4.13 on.

Digging into expressions

After some experiments, I find not all expressions work in ErrorDocument. But variable as specified in expression's BNF notation can be interpreted in ErrorDocument,

variable    ::= "%{" varname "}"
              | "%{" funcname ":" funcargs "}"

(rebackref might work too, but I'm not sure how that can be used in ErrorDocument.)

Now we only need some empty variables or functions that can evaluate to an empty string.

And there is an empty string expression, in the form of a function,

unescape

Unescape %hex encoded string, leaving encoded slashes alone; return an empty string if %00 is found

So here is the solution to return an empty response body,

ErrorDocument 200 %{unescape:%00}

More about ErrorDocument

Why is there a non-empty response body as mentioned in the question in the first place?

I find this in apache's ErrorDocument Directive,

In the event of a problem or error, Apache httpd can be configured to do one of four things,

  1. output a simple hardcoded error message
  2. output a customized message
  3. internally redirect to a local URL-path to handle the problem/error
  4. redirect to an external URL to handle the problem/error

Since the error document for 200 is not specified, Apache decided to use the hardcoded error message for 200.

Details about the hardcoded error message is not in the documentation and has to be foundd in the source code. But I guess there is no such error message for 200, and so we have this internal error/misconfiguration thing.

The exact same response as mentioned in the question can be obtained by

ErrorDocument 200 default

since Apache's ErrorDocument Directive mentions this,

Additionally, the special value default can be used to specify Apache httpd's simple hardcoded message.

like image 24
LZY Avatar answered Oct 28 '22 17:10

LZY