diff --git a/btpd/tracker_req.c b/btpd/tracker_req.c
index c21bd5b..9195e08 100644
--- a/btpd/tracker_req.c
+++ b/btpd/tracker_req.c
@@ -27,11 +27,12 @@ enum timer_type {
 };
 
 struct tracker {
-    struct mi_announce *ann;
     enum timer_type ttype;
     enum tr_event event;
     int interval;
     unsigned nerrors;
+    int tier, url;
+    struct mi_announce *ann;
     struct http *req;
     struct event timer;
 };
@@ -129,6 +130,33 @@ tr_set_stopped(struct torrent *tp)
     torrent_on_tr_stopped(tp);
 }
 
+static char *
+get_url(struct tracker *tr)
+{
+    return tr->ann->tiers[tr->tier].urls[tr->url];
+}
+
+static void
+good_url(struct tracker *tr)
+{
+    char *set = tr->ann->tiers[tr->tier].urls[tr->url], *hold;
+    for (int i = 0; i <= tr->url; i++) {
+        hold = tr->ann->tiers[tr->tier].urls[i];
+        tr->ann->tiers[tr->tier].urls[i] = set;
+        set = hold;
+    }
+    tr->tier = 0;
+    tr->url = 0;
+}
+
+static void
+next_url(struct tracker *tr)
+{
+    tr->url = (tr->url + 1) % tr->ann->tiers[tr->tier].nurls;
+    if (tr->url == 0)
+        tr->tier = (tr->tier + 1) % tr->ann->ntiers;
+}
+
 static void
 http_cb(struct http *req, struct http_res *res, void *arg)
 {
@@ -138,6 +166,7 @@ http_cb(struct http *req, struct http_res *res, void *arg)
     tr->req = NULL;
     if (res->res == HRES_OK && parse_reply(tp, res->content, res->length,
             tr->event != TR_EV_STOPPED) == 0) {
+        good_url(tr);
         tr->nerrors = 0;
         tr->ttype = TIMER_INTERVAL;
         btpd_ev_add(&tr->timer, (& (struct timeval) { tr->interval, 0 }));
@@ -168,6 +197,7 @@ timer_cb(int fd, short type, void *arg)
             break;
         }
     case TIMER_RETRY:
+        next_url(tr);
         tr_send(tp, tr->event);
         break;
     case TIMER_INTERVAL:
@@ -190,7 +220,7 @@ tr_send(struct torrent *tp, enum tr_event event)
     if (tr->ttype == TIMER_TIMEOUT)
         http_cancel(tr->req);
 
-    if ((busy_secs = http_server_busy_time(tr->ann->tiers[0].urls[0], 3)) > 0) {
+    if ((busy_secs = http_server_busy_time(get_url(tr), 3)) > 0) {
         tr->ttype = TIMER_RETRY;
         btpd_ev_add(&tr->timer, (& (struct timeval) { busy_secs, 0 }));
         return;
@@ -199,7 +229,7 @@ tr_send(struct torrent *tp, enum tr_event event)
     tr->ttype = TIMER_TIMEOUT;
     btpd_ev_add(&tr->timer, REQ_TIMEOUT);
 
-    qc = (strchr(tr->ann->tiers[0].urls[0], '?') == NULL) ? '?' : '&';
+    qc = (strchr(get_url(tr), '?') == NULL) ? '?' : '&';
 
     for (int i = 0; i < 20; i++)
         snprintf(e_hash + i * 3, 4, "%%%.2x", tp->tl->hash[i]);
@@ -209,7 +239,7 @@ tr_send(struct torrent *tp, enum tr_event event)
     http_get(&tr->req, http_cb, tp,
         "%s%cinfo_hash=%s&peer_id=%s&port=%d&uploaded=%llu"
         "&downloaded=%llu&left=%llu&compact=1%s%s",
-        tr->ann->tiers[0].urls[0], qc, e_hash, e_id, net_port,
+        get_url(tr), qc, e_hash, e_id, net_port,
         tp->net->uploaded, tp->net->downloaded,
         (long long)tp->total_length - cm_content(tp),
         event == TR_EV_EMPTY ? "" : "&event=", m_events[event]);
@@ -218,18 +248,9 @@ tr_send(struct torrent *tp, enum tr_event event)
 int
 tr_create(struct torrent *tp, const char *mi)
 {
-    struct mi_announce *ann = mi_announce(mi);
-    if (ann == NULL)
-        btpd_err("out of memory.\n");
-    if (strncmp(ann->tiers[0].urls[0],
-            "http://", sizeof("http://") - 1) != 0) {
-        btpd_log(BTPD_L_ERROR,
-            "btpd currently has no support for the protocol specified in "
-            "'%s'.\n", ann->tiers[0].urls[0]);
-        return EINVAL;
-    }
     tp->tr = btpd_calloc(1, sizeof(*tp->tr));
-    tp->tr->ann = ann;
+    if ((tp->tr->ann = mi_announce(mi)) == NULL)
+        btpd_err("Out of memory.\n");
     evtimer_set(&tp->tr->timer, timer_cb, tp);
     return 0;
 }