Dart RedirectInfo
last modified October 18, 2026
The RedirectInfo class in Dart provides information about HTTP
redirects that occur during network requests. It's part of Dart's
dart:io library for server-side and command-line applications.
RedirectInfo contains details about redirect responses including status code, location, and method. It helps track and analyze redirect chains in HTTP communication.
Basic Definition
RedirectInfo is an immutable class representing a single HTTP
redirect. It's typically obtained from HttpClientResponse redirects.
Key properties include statusCode, method, and location. These help understand and process redirects in HTTP clients and servers.
Basic RedirectInfo Usage
This example shows how to access RedirectInfo from an HTTP client response. A local server issues a redirect so the example is self-contained.
import 'dart:io';
Future<void> main() async {
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
server.listen((request) {
if (request.uri.path == '/old') {
request.response
..statusCode = HttpStatus.movedPermanently
..headers.set(HttpHeaders.locationHeader, '/new');
} else {
request.response.write('Welcome');
}
request.response.close();
});
var client = HttpClient();
try {
var url = Uri.parse('http://${server.address.address}:${server.port}/old');
var request = await client.getUrl(url);
var response = await request.close();
if (response.redirects.isNotEmpty) {
var redirect = response.redirects.first;
print('Redirected to: ${redirect.location}');
print('Status code: ${redirect.statusCode}');
print('Method: ${redirect.method}');
}
await response.drain<void>();
} finally {
client.close(force: true);
await server.close(force: true);
}
}
We create an HTTP client and check for redirects in the response. The first redirect's details are printed. The client follows redirects automatically by default, so the response is the final page and redirects records what happened.
$ dart main.dart Redirected to: /new Status code: 301 Method: GET
Analyzing Redirect Chains
This example demonstrates examining a complete redirect chain.
import 'dart:io';
Future<void> main() async {
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
server.listen((request) {
if (request.uri.path == '/a') {
request.response
..statusCode = HttpStatus.movedPermanently
..headers.set(HttpHeaders.locationHeader, '/b');
} else if (request.uri.path == '/b') {
request.response
..statusCode = HttpStatus.found
..headers.set(HttpHeaders.locationHeader, '/c');
} else {
request.response.write('Done');
}
request.response.close();
});
var client = HttpClient();
try {
var url = Uri.parse('http://${server.address.address}:${server.port}/a');
var request = await client.getUrl(url);
request.maxRedirects = 10;
var response = await request.close();
print('Final URL: ${response.redirects.last.location}');
print('Redirect chain length: ${response.redirects.length}');
for (var redirect in response.redirects) {
print('${redirect.statusCode} ${redirect.method} -> ${redirect.location}');
}
await response.drain<void>();
} finally {
client.close(force: true);
await server.close(force: true);
}
}
We print each redirect in the chain with its status code and method. Setting maxRedirects on the request limits how many redirects the client follows.
$ dart main.dart Final URL: /c Redirect chain length: 2 301 GET -> /b 302 GET -> /c
Following Redirects Manually
This example shows manual redirect following using RedirectInfo.
import 'dart:io';
Future<void> main() async {
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
server.listen((request) {
if (request.uri.path == '/start') {
request.response
..statusCode = HttpStatus.found
..headers.set(HttpHeaders.locationHeader, '/final');
} else {
request.response.write('Done');
}
request.response.close();
});
var client = HttpClient();
var maxRedirects = 5;
try {
var url = Uri.parse('http://${server.address.address}:${server.port}/start');
var request = await client.getUrl(url);
request.followRedirects = false;
var response = await request.close();
var count = 0;
while (response.isRedirect && count < maxRedirects) {
await response.drain<void>();
response = await response.redirect();
count++;
print('Redirect $count: ${response.redirects.last.location}');
}
var finalUrl = response.redirects.isEmpty
? url.path
: response.redirects.last.location;
print('Final URL: $finalUrl');
await response.drain<void>();
} finally {
client.close(force: true);
await server.close(force: true);
}
}
We disable automatic redirects and follow them with response.redirect() up to a maximum limit. Each manual step adds a RedirectInfo to response.redirects.
$ dart main.dart Redirect 1: /final Final URL: /final
Handling Different Redirect Types
This example shows how to handle various redirect status codes.
import 'dart:io';
Future<void> main() async {
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
server.listen((request) {
if (request.uri.path == '/temporary') {
request.response
..statusCode = HttpStatus.found
..headers.set(HttpHeaders.locationHeader, '/new-location');
} else {
request.response.write('Welcome');
}
request.response.close();
});
var client = HttpClient();
try {
var url =
Uri.parse('http://${server.address.address}:${server.port}/temporary');
var request = await client.getUrl(url);
var response = await request.close();
if (response.redirects.isNotEmpty) {
var redirect = response.redirects.first;
switch (redirect.statusCode) {
case HttpStatus.movedPermanently:
print('Permanent redirect to ${redirect.location}');
break;
case HttpStatus.found:
print('Temporary redirect to ${redirect.location}');
break;
case HttpStatus.seeOther:
print('See other (change method to GET) to ${redirect.location}');
break;
default:
print('Unknown redirect type: ${redirect.statusCode}');
}
}
await response.drain<void>();
} finally {
client.close(force: true);
await server.close(force: true);
}
}
We check the status code to determine the redirect type. Different HTTP status codes indicate different redirect semantics.
$ dart main.dart Temporary redirect to /new-location
Creating Custom Redirect Policies
This example demonstrates implementing a custom redirect policy.
import 'dart:io';
class SecureRedirectPolicy {
bool allowRedirect(Uri location) {
if (location.scheme.isNotEmpty && location.scheme != 'https') {
print('Blocking insecure redirect to $location');
return false;
}
return true;
}
}
Future<void> main() async {
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
server.listen((request) {
request.response
..statusCode = HttpStatus.movedPermanently
..headers.set(HttpHeaders.locationHeader,
'http://insecure.example.com/login');
request.response.close();
});
var client = HttpClient();
var policy = SecureRedirectPolicy();
try {
var url = Uri.parse('http://${server.address.address}:${server.port}/login');
var request = await client.getUrl(url);
request.followRedirects = false;
var response = await request.close();
var location = response.headers.value(HttpHeaders.locationHeader);
if (location != null && !policy.allowRedirect(Uri.parse(location))) {
print('Aborting due to redirect policy violation');
}
await response.drain<void>();
} finally {
client.close(force: true);
await server.close(force: true);
}
}
We inspect the Location header before following a redirect. Paths and HTTPS URLs are allowed; absolute HTTP URLs are rejected by the policy.
$ dart main.dart Blocking insecure redirect to http://insecure.example.com/login Aborting due to redirect policy violation
Best Practices
- Limit redirects: Set maxRedirects to prevent infinite loops
- Validate URLs: Check redirect locations for security
- Preserve method: Handle 307/308 status codes correctly
- Log redirects: Record redirect chains for debugging
- Handle errors: Prepare for malformed redirect locations
Source
Dart RedirectInfo Documentation
This tutorial covered Dart's RedirectInfo class with practical examples showing redirect handling, chain analysis, and custom security policies.
Author
List all Dart tutorials.