diff --git a/server/cache.go b/server/cache.go
index df4c2c9ef3f13e3148a5dee83faf0190faa9c8a4..fb13095cf1d168b20d1d38d6b30a634eff2845aa 100644
--- a/server/cache.go
+++ b/server/cache.go
@@ -7,7 +7,7 @@ import (
 
 type cache interface {
 	AddMessage(m *message) error
-	Messages(topic string, since time.Time) ([]*message, error)
+	Messages(topic string, since sinceTime) ([]*message, error)
 	MessageCount(topic string) (int, error)
 	Topics() (map[string]*topic, error)
 	Prune(keep time.Duration) error
diff --git a/server/cache_mem.go b/server/cache_mem.go
index 6c3a94f1665adba9def526857cf085fb68ca6bed..83b0f36d6a5f9ec22bdeffdfca491140a3cde3e4 100644
--- a/server/cache_mem.go
+++ b/server/cache_mem.go
@@ -29,7 +29,7 @@ func (s *memCache) AddMessage(m *message) error {
 	return nil
 }
 
-func (s *memCache) Messages(topic string, since time.Time) ([]*message, error) {
+func (s *memCache) Messages(topic string, since sinceTime) ([]*message, error) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 	if _, ok := s.messages[topic]; !ok {
@@ -38,7 +38,7 @@ func (s *memCache) Messages(topic string, since time.Time) ([]*message, error) {
 	messages := make([]*message, 0) // copy!
 	for _, m := range s.messages[topic] {
 		msgTime := time.Unix(m.Time, 0)
-		if msgTime == since || msgTime.After(since) {
+		if msgTime == since.Time() || msgTime.After(since.Time()) {
 			messages = append(messages, m)
 		}
 	}
diff --git a/server/cache_sqlite.go b/server/cache_sqlite.go
index 2a07f74162a2cc77b15583db8c4853dd5dd4aad1..89e932e81b52ff9f4d1f8548fb62e73e28863fb0 100644
--- a/server/cache_sqlite.go
+++ b/server/cache_sqlite.go
@@ -55,8 +55,8 @@ func (c *sqliteCache) AddMessage(m *message) error {
 	return err
 }
 
-func (c *sqliteCache) Messages(topic string, since time.Time) ([]*message, error) {
-	rows, err := c.db.Query(selectMessagesSinceTimeQuery, topic, since.Unix())
+func (c *sqliteCache) Messages(topic string, since sinceTime) ([]*message, error) {
+	rows, err := c.db.Query(selectMessagesSinceTimeQuery, topic, since.Time().Unix())
 	if err != nil {
 		return nil, err
 	}
diff --git a/server/server.go b/server/server.go
index 27f0eb85b8351704905abb95abb703bab16b202c..1cb185cbf84188a4167b267a6e065ef2f3b600f8 100644
--- a/server/server.go
+++ b/server/server.go
@@ -53,6 +53,25 @@ type indexPage struct {
 	CacheDuration string
 }
 
+type sinceTime time.Time
+
+func (t sinceTime) IsAll() bool {
+	return t == sinceAllMessages
+}
+
+func (t sinceTime) IsNone() bool {
+	return t == sinceNoMessages
+}
+
+func (t sinceTime) Time() time.Time {
+	return time.Time(t)
+}
+
+var (
+	sinceAllMessages = sinceTime(time.Unix(0, 0))
+	sinceNoMessages  = sinceTime(time.Unix(1, 0))
+)
+
 const (
 	messageLimit = 512
 )
@@ -318,8 +337,8 @@ func (s *Server) handleSubscribe(w http.ResponseWriter, r *http.Request, v *visi
 	}
 }
 
-func (s *Server) sendOldMessages(t *topic, since time.Time, sub subscriber) error {
-	if since.IsZero() {
+func (s *Server) sendOldMessages(t *topic, since sinceTime, sub subscriber) error {
+	if since.IsNone() {
 		return nil
 	}
 	messages, err := s.cache.Messages(t.id, since)
@@ -334,17 +353,27 @@ func (s *Server) sendOldMessages(t *topic, since time.Time, sub subscriber) erro
 	return nil
 }
 
-func parseSince(r *http.Request) (time.Time, error) {
+// parseSince returns a timestamp identifying the time span from which cached messages should be received.
+//
+// Values in the "since=..." parameter can be either a unix timestamp or a duration (e.g. 12h), or
+// "all" for all messages.
+func parseSince(r *http.Request) (sinceTime, error) {
 	if !r.URL.Query().Has("since") {
-		return time.Time{}, nil
+		if r.URL.Query().Has("poll") {
+			return sinceAllMessages, nil
+		}
+		return sinceNoMessages, nil
+	}
+	if r.URL.Query().Get("since") == "all" {
+		return sinceAllMessages, nil
 	}
-	if since, err := strconv.ParseInt(r.URL.Query().Get("since"), 10, 64); err == nil {
-		return time.Unix(since, 0), nil
+	if s, err := strconv.ParseInt(r.URL.Query().Get("since"), 10, 64); err == nil {
+		return sinceTime(time.Unix(s, 0)), nil
 	}
 	if d, err := time.ParseDuration(r.URL.Query().Get("since")); err == nil {
-		return time.Now().Add(-1 * d), nil
+		return sinceTime(time.Now().Add(-1 * d)), nil
 	}
-	return time.Time{}, errHTTPBadRequest
+	return sinceNoMessages, errHTTPBadRequest
 }
 
 func (s *Server) handleOptions(w http.ResponseWriter, r *http.Request) error {
diff --git a/server/static/js/app.js b/server/static/js/app.js
index d4de8de368d06b95337a5fc1a45cfa14735f313d..ea9e335e111917bd4b175c399ce4ea3fa3b5aeb7 100644
--- a/server/static/js/app.js
+++ b/server/static/js/app.js
@@ -118,7 +118,7 @@ const test = (topic) => {
 };
 
 const fetchCachedMessages = async (topic) => {
-    const topicJsonUrl = `/${topic}/json?poll=1&since=12h`; // Poll!
+    const topicJsonUrl = `/${topic}/json?poll=1`; // Poll!
     for await (let line of makeTextFileLineIterator(topicJsonUrl)) {
         const message = JSON.parse(line);
         topics[topic]['messages'].push(message);