
Security News
Another Round of TEA Protocol Spam Floods npm, But It’s Not a Worm
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.
@remix-run/headers
Advanced tools
Tired of manually parsing and stringifying HTTP header values in JavaScript? headers supercharges the standard Headers interface, providing a robust toolkit for effortless and type-safe header manipulation.
HTTP headers are packed with critical information—from content negotiation and caching directives to authentication tokens and file metadata. While the native Headers API provides a basic string-based interface, it leaves the complexities of parsing specific header formats (like Accept, Content-Type, or Set-Cookie) entirely up to you.
headers solves this by offering:
Headers object, it can be used anywhere a Headers object is expected, providing progressive enhancement to your existing code.Headers object.Unlock a more powerful and elegant way to work with HTTP headers in your JavaScript and TypeScript projects!
npm install @remix-run/headers
The following should give you a sense of what kinds of things you can do with this library:
import Headers from '@remix-run/headers'
let headers = new Headers()
// Accept
headers.accept = 'text/html, text/*;q=0.9'
headers.accept.mediaTypes // [ 'text/html', 'text/*' ]
Object.fromEntries(headers.accept.entries()) // { 'text/html': 1, 'text/*': 0.9 }
headers.accept.accepts('text/html') // true
headers.accept.accepts('text/plain') // true
headers.accept.accepts('image/jpeg') // false
headers.accept.getPreferred(['text/plain', 'text/html']) // 'text/html'
headers.accept.set('text/plain', 0.9)
headers.accept.set('text/*', 0.8)
headers.get('Accept') // 'text/html,text/plain;q=0.9,text/*;q=0.8'
// Accept-Encoding
headers.acceptEncoding = 'gzip, deflate;q=0.8'
headers.acceptEncoding.encodings // [ 'gzip', 'deflate' ]
Object.fromEntries(headers.acceptEncoding.entries()) // { 'gzip': 1, 'deflate': 0.8 }
headers.acceptEncoding.accepts('gzip') // true
headers.acceptEncoding.accepts('br') // false
headers.acceptEncoding.getPreferred(['gzip', 'deflate']) // 'gzip'
// Accept-Language
headers.acceptLanguage = 'en-US, en;q=0.9'
headers.acceptLanguage.languages // [ 'en-us', 'en' ]
Object.fromEntries(headers.acceptLanguage.entries()) // { 'en-us': 1, en: 0.9 }
headers.acceptLanguage.accepts('en') // true
headers.acceptLanguage.accepts('ja') // false
headers.acceptLanguage.getPreferred(['en-US', 'en-GB']) // 'en-US'
headers.acceptLanguage.getPreferred(['en', 'fr']) // 'en'
// Accept-Ranges
headers.acceptRanges = 'bytes'
// Connection
headers.connection = 'close'
// Content-Type
headers.contentType = 'application/json; charset=utf-8'
headers.contentType.mediaType // "application/json"
headers.contentType.charset // "utf-8"
headers.contentType.charset = 'iso-8859-1'
headers.get('Content-Type') // "application/json; charset=iso-8859-1"
// Content-Disposition
headers.contentDisposition =
'attachment; filename="example.pdf"; filename*=UTF-8\'\'%E4%BE%8B%E5%AD%90.pdf'
headers.contentDisposition.type // 'attachment'
headers.contentDisposition.filename // 'example.pdf'
headers.contentDisposition.filenameSplat // 'UTF-8\'\'%E4%BE%8B%E5%AD%90.pdf'
headers.contentDisposition.preferredFilename // '例子.pdf'
// Cookie
headers.cookie = 'session_id=abc123; user_id=12345'
headers.cookie.get('session_id') // 'abc123'
headers.cookie.get('user_id') // '12345'
headers.cookie.set('theme', 'dark')
headers.get('Cookie') // 'session_id=abc123; user_id=12345; theme=dark'
// Host
headers.host = 'example.com'
// If-None-Match
headers.ifNoneMatch = ['67ab43', '54ed21']
headers.get('If-None-Match') // '"67ab43", "54ed21"'
// Last-Modified
headers.lastModified = new Date()
// or headers.lastModified = new Date().getTime();
headers.get('Last-Modified') // 'Fri, 20 Dec 2024 08:08:05 GMT'
// Location
headers.location = 'https://example.com'
// Referer
headers.referer = 'https://example.com/'
// Set-Cookie
headers.setCookie = ['session_id=abc123; Path=/; HttpOnly']
headers.setCookie[0].name // 'session_id'
headers.setCookie[0].value // 'abc123'
headers.setCookie[0].path // '/'
headers.setCookie[0].httpOnly // true
// Modifying Set-Cookie attributes
headers.setCookie[0].maxAge = 3600
headers.setCookie[0].secure = true
headers.get('Set-Cookie') // 'session_id=abc123; Path=/; HttpOnly; Max-Age=3600; Secure'
// Setting multiple cookies is easy, it's just an array
headers.setCookie.push('user_id=12345; Path=/api; Secure')
// or headers.setCookie = [...headers.setCookie, '...']
// Accessing multiple Set-Cookie headers
for (let cookie of headers.getSetCookie()) {
console.log(cookie)
}
// session_id=abc123; Path=/; HttpOnly; Max-Age=3600; Secure
// user_id=12345; Path=/api; Secure
Headers can be initialized with an object config:
let headers = new Headers({
contentType: {
mediaType: 'text/html',
charset: 'utf-8',
},
setCookie: [
{ name: 'session', value: 'abc', path: '/' },
{ name: 'theme', value: 'dark', expires: new Date('2021-12-31T23:59:59Z') },
],
})
console.log(`${headers}`)
// Content-Type: text/html; charset=utf-8
// Set-Cookie: session=abc; Path=/
// Set-Cookie: theme=dark; Expires=Fri, 31 Dec 2021 23:59:59 GMT
Headers works just like DOM's Headers (it's a subclass) so you can use them anywhere you need a Headers.
import Headers from '@remix-run/headers'
// Use in a fetch()
let response = await fetch('https://example.com', {
headers: new Headers(),
})
// Convert from DOM Headers
let headers = new Headers(response.headers)
headers.set('Content-Type', 'text/html')
headers.get('Content-Type') // "text/html"
If you're familiar with using DOM Headers, everything works as you'd expect.
Headers are iterable:
let headers = new Headers({
'Content-Type': 'application/json',
'X-API-Key': 'secret-key',
'Accept-Language': 'en-US,en;q=0.9',
})
for (let [name, value] of headers) {
console.log(`${name}: ${value}`)
}
// Content-Type: application/json
// X-Api-Key: secret-key
// Accept-Language: en-US,en;q=0.9
If you're assembling HTTP messages, you can easily convert to a multiline string suitable for using as a Request/Response header block:
let headers = new Headers({
'Content-Type': 'application/json',
'Accept-Language': 'en-US,en;q=0.9',
})
console.log(`${headers}`)
// Content-Type: application/json
// Accept-Language: en-US,en;q=0.9
In addition to the high-level Headers API, headers also provides a rich set of primitives you can use to work with just about any complex HTTP header value. Each header class includes a spec-compliant parser (the constructor), stringifier (toString), and getters/setters for all relevant attributes. Classes for headers that contain a list of fields, like Cookie, are iterable.
If you need support for a header that isn't listed here, please send a PR! The goal is to have first-class support for all common HTTP headers.
import { Accept } from '@remix-run/headers'
let header = new Accept('text/html;text/*;q=0.9')
header.has('text/html') // true
header.has('text/plain') // false
header.accepts('text/html') // true
header.accepts('text/plain') // true
header.accepts('text/*') // true
header.accepts('image/jpeg') // false
header.getPreferred(['text/html', 'text/plain']) // 'text/html'
for (let [mediaType, quality] of header) {
// ...
}
// Alternative init styles
let header = new Accept({ 'text/html': 1, 'text/*': 0.9 })
let header = new Accept(['text/html', ['text/*', 0.9]])
import { AcceptEncoding } from '@remix-run/headers'
let header = new AcceptEncoding('gzip,deflate;q=0.9')
header.has('gzip') // true
header.has('br') // false
header.accepts('gzip') // true
header.accepts('deflate') // true
header.accepts('identity') // true
header.accepts('br') // true
header.getPreferred(['gzip', 'deflate']) // 'gzip'
for (let [encoding, weight] of header) {
// ...
}
// Alternative init styles
let header = new AcceptEncoding({ gzip: 1, deflate: 0.9 })
let header = new AcceptEncoding(['gzip', ['deflate', 0.9]])
import { AcceptLanguage } from '@remix-run/headers'
let header = new AcceptLanguage('en-US,en;q=0.9')
header.has('en-US') // true
header.has('en-GB') // false
header.accepts('en-US') // true
header.accepts('en-GB') // true
header.accepts('en') // true
header.accepts('fr') // true
header.getPreferred(['en-US', 'en-GB']) // 'en-US'
header.getPreferred(['en', 'fr']) // 'en'
for (let [language, quality] of header) {
// ...
}
// Alternative init styles
let header = new AcceptLanguage({ 'en-US': 1, en: 0.9 })
let header = new AcceptLanguage(['en-US', ['en', 0.9]])
import { CacheControl } from '@remix-run/headers'
let header = new CacheControl('public, max-age=3600, s-maxage=3600')
header.public // true
header.maxAge // 3600
header.sMaxage // 3600
// Alternative init style
let header = new CacheControl({ public: true, maxAge: 3600 })
// Full set of supported properties
header.public // true/false
header.private // true/false
header.noCache // true/false
header.noStore // true/false
header.noTransform // true/false
header.mustRevalidate // true/false
header.proxyRevalidate // true/false
header.maxAge // number
header.sMaxage // number
header.minFresh // number
header.maxStale // number
header.onlyIfCached // true/false
header.immutable // true/false
header.staleWhileRevalidate // number
header.staleIfError // number
import { ContentDisposition } from '@remix-run/headers'
let header = new ContentDisposition('attachment; name=file1; filename=file1.txt')
header.type // "attachment"
header.name // "file1"
header.filename // "file1.txt"
header.preferredFilename // "file1.txt"
// Alternative init style
let header = new ContentDisposition({
type: 'attachment',
name: 'file1',
filename: 'file1.txt',
})
import { ContentType } from '@remix-run/headers'
let header = new ContentType('text/html; charset=utf-8')
header.mediaType // "text/html"
header.boundary // undefined
header.charset // "utf-8"
// Alternative init style
let header = new ContentType({
mediaType: 'multipart/form-data',
boundary: '------WebKitFormBoundary12345',
charset: 'utf-8',
})
import { Cookie } from '@remix-run/headers'
let header = new Cookie('theme=dark; session_id=123')
header.get('theme') // "dark"
header.set('theme', 'light')
header.delete('theme')
header.has('session_id') // true
// Iterate over cookie name/value pairs
for (let [name, value] of header) {
// ...
}
// Alternative init styles
let header = new Cookie({ theme: 'dark', session_id: '123' })
let header = new Cookie([
['theme', 'dark'],
['session_id', '123'],
])
import { IfNoneMatch } from '@remix-run/headers'
let header = new IfNoneMatch('"67ab43", "54ed21"')
header.has('67ab43') // true
header.has('21ba69') // false
header.matches('"67ab43"') // true
// Alternative init style
let header = new IfNoneMatch(['67ab43', '54ed21'])
let header = new IfNoneMatch({
tags: ['67ab43', '54ed21'],
})
import { SetCookie } from '@remix-run/headers'
let header = new SetCookie('session_id=abc; Domain=example.com; Path=/; Secure; HttpOnly')
header.name // "session_id"
header.value // "abc"
header.domain // "example.com"
header.path // "/"
header.secure // true
header.httpOnly // true
header.sameSite // undefined
header.maxAge // undefined
header.expires // undefined
// Alternative init styles
let header = new SetCookie({
name: 'session_id',
value: 'abc',
domain: 'example.com',
path: '/',
secure: true,
httpOnly: true,
})
fetch-proxy - Build HTTP proxy servers using the web fetch APInode-fetch-server - Build HTTP servers on Node.js using the web fetch APISee LICENSE
FAQs
A toolkit for working with HTTP headers in JavaScript
The npm package @remix-run/headers receives a total of 8,785 weekly downloads. As such, @remix-run/headers popularity was classified as popular.
We found that @remix-run/headers demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.

Security News
PyPI adds Trusted Publishing support for GitLab Self-Managed as adoption reaches 25% of uploads

Research
/Security News
A malicious Chrome extension posing as an Ethereum wallet steals seed phrases by encoding them into Sui transactions, enabling full wallet takeover.