httpuv emits duplicate 'Date' response header when one is already supplied
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 64/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Domain
- backend-api-design
Research direction
Read src/httpresponse.h and the header handling in src/webapplication.cpp, starting with listToResponse() and its use of addHeader() and setHeader(). The issue also notes a separate static path for custom headers, which may need investigation. Reproduce with the provided R example and curl; done means the response has exactly one valid Date field whether the application supplies one or omits it.
Written by the indexing model from the issue text.
Description
Description
When an application response already includes a Date header, httpuv 1.6.17 sends a second Date field line, instead of preserving or replacing the supplied value.
Impact
RFC 9110 Sections 5.3 and 6.6.1 make Date a single HTTP-date field, not a list. Because Date is not list-valued, combining repeated lines with commas would produce a value that does not match the Date grammar. Clients, caches, or intermediaries may therefore reject or ignore the timestamp. This is an interoperability/conformance issue rather than an immediate security issue.
If the two dates differ, recipients that recover by selecting one field line may disagree about when the response originated; caches also use Date when calculating response age, so this can lead to inconsistent freshness handling.
Background
We found this while inspecting the raw response headers from our application during a cache-policy review. The application uses Plumber2, which runs on Fiery and uses reqres for request/response handling. The first Date is added by reqres 1.2.0 when it formats the response; our route does not set it. httpuv then adds a second Date to the wire response.
I searched the httpuv, Fiery, reqres, and Plumber2 issue trackers for matching reports and did not find one.
We reduced the behavior to the httpuv-only example below to isolate the HTTP server layer.
Environment
- R 4.6.2
- httpuv 1.6.17
Steps to reproduce
Save this as date-header-mwe.R:
message("Starting server at http://127.0.0.1:8081/ (press Ctrl+C to stop)")
httpuv::runServer(
host = "127.0.0.1",
port = 8081,
app = list(
call = function(req) {
list(
status = 200L,
headers = list(
# simulate an application-supplied Date. httpuv will add another Date automatically.
Date = format(Sys.time(), "%a, %d %b %Y %H:%M:%S GMT", tz = "GMT")
),
body = ""
)
}
)
)
Start the server:
Rscript date-header-mwe.R
In another process, send a request to the server:
curl -sS -D - -o /dev/null http://127.0.0.1:8081/
Actual behavior
HTTP/1.1 200 OK
Date: Thu, 05 Nov 2026 17:01:29 GMT
Date: Thu, 05 Nov 2026 17:01:29 GMT
Content-Length: 0
The date and header order may vary, but the response contains two Date field lines.
Expected behavior
HTTP/1.1 200 OK
Date: Thu, 05 Nov 2026 17:01:29 GMT
Content-Length: 0
The response should contain one valid Date field line. The server may use its own timestamp or an application-supplied timestamp, but should not emit both.
Possible patch
I'm personally new to httpuv, but to be as solution-oriented as possible, here's an AI-generated patch, which may fix this issue...
In v1.6.17, HttpResponse adds a default Date in its constructor (src/httpresponse.h). The listToResponse() path in src/webapplication.cpp then appends each R-supplied response header with addHeader(). For a supplied Date, using the existing case-insensitive setHeader() method instead would replace the default while preserving append behavior for other headers.
Illustrative patch for listToResponse() (a regression test should cover supplied and omitted Date):
diff --git a/src/webapplication.cpp b/src/webapplication.cpp
--- a/src/webapplication.cpp
+++ b/src/webapplication.cpp
@@
- pResp->addHeader(
- std::string((char*)headerNames[i], headerNames[i].size()),
- Rcpp::as<std::string>(responseHeaders[i]));
+ const std::string name((char*)headerNames[i], headerNames[i].size());
+ const std::string value = Rcpp::as<std::string>(responseHeaders[i]);
+ if (to_lower(name) == "date") {
+ pResp->setHeader(name, value);
+ } else {
+ pResp->addHeader(name, value);
+ }
This covers the R response path shown above. Static-path custom headers are added through a separate code path and would need the same Date replacement if they are intended to support user-supplied Date values too.
- Dominant language
- C
- Stars
- 252
- Forks
- 87
- PR merge metrics
- No merged PRs in 30d
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from rstudio/httpuv
-
Difficulty 4/5 3-5 days Newbie friendliness 46/100
-
Difficulty 5/5 Over a week Newbie friendliness 30/100
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
-
feature good first issue :heart: help wanted :heart: upkeep
Difficulty 3/5 1-2 days Newbie friendliness 25/100
Similar issues
-
polygonToCells CLI silently drops vertices when whitespace precedes a coordinate's closing bracketOpen
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
issue: bug report
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 2 days
-
good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
dfa1/rocksdb-ffm#292 ·
Maintainers usually reply within 1 day
-
SPIDevice selects an active-high device as soon as it is createdPossibly taken @mikeysklar claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
adafruit/circuitpython#11869 ·
Maintainers usually reply within 1 day