ZetCode

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.

main.dart
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.

main.dart
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.

main.dart
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.

main.dart
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.

main.dart
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

Source

Dart RedirectInfo Documentation

This tutorial covered Dart's RedirectInfo class with practical examples showing redirect handling, chain analysis, and custom security policies.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.