diff --git a/frontend_tests/node_tests/dispatch.js b/frontend_tests/node_tests/dispatch.js
index 77eb3d3101..909e3255ce 100644
--- a/frontend_tests/node_tests/dispatch.js
+++ b/frontend_tests/node_tests/dispatch.js
@@ -676,6 +676,20 @@ run_test("update_display_settings", (override) => {
         assert(page_params.color_scheme, 3);
     }
 
+    {
+        event = event_fixtures.update_display_settings__default_view_recent_topics;
+        page_params.default_view = "all_messages";
+        dispatch(event);
+        assert(page_params.default_view, "recent_topics");
+    }
+
+    {
+        event = event_fixtures.update_display_settings__default_view_all_messages;
+        page_params.default_view = "recent_topics";
+        dispatch(event);
+        assert(page_params.default_view, "all_messages");
+    }
+
     {
         const stub = make_stub();
         event = event_fixtures.update_display_settings__color_scheme_automatic;
diff --git a/frontend_tests/node_tests/lib/events.js b/frontend_tests/node_tests/lib/events.js
index afd2884164..5364903e8d 100644
--- a/frontend_tests/node_tests/lib/events.js
+++ b/frontend_tests/node_tests/lib/events.js
@@ -610,6 +610,20 @@ exports.fixtures = {
         user: test_user.email,
     },
 
+    update_display_settings__default_view_all_messages: {
+        type: "update_display_settings",
+        setting_name: "default_view",
+        setting: 1,
+        user: test_user.email,
+    },
+
+    update_display_settings__default_view_recent_topics: {
+        type: "update_display_settings",
+        setting_name: "default_view",
+        setting: "recent_topics",
+        user: test_user.email,
+    },
+
     update_display_settings__demote_inactive_streams: {
         type: "update_display_settings",
         setting_name: "demote_inactive_streams",
diff --git a/static/js/hashchange.js b/static/js/hashchange.js
index 4fdc4baabf..c5d5ec7069 100644
--- a/static/js/hashchange.js
+++ b/static/js/hashchange.js
@@ -59,7 +59,7 @@ function maybe_hide_recent_topics() {
 }
 
 export function in_recent_topics_hash() {
-    return ["recent_topics", "#", ""].includes(window.location.hash);
+    return ["#recent_topics"].includes(window.location.hash);
 }
 
 export function changehash(newhash) {
@@ -114,6 +114,10 @@ function is_overlay_hash(hash) {
     return overlay_list.includes(main_hash);
 }
 
+export function show_default_view() {
+    window.location.hash = page_params.default_view;
+}
+
 // Returns true if this function performed a narrow
 function do_hashchange_normal(from_reload) {
     message_viewport.stop_auto_scrolling();
@@ -130,8 +134,7 @@ function do_hashchange_normal(from_reload) {
             if (operators === undefined) {
                 // If the narrow URL didn't parse, clear
                 // window.location.hash and send them to the home tab
-                set_hash("#all_messages");
-                activate_home_tab();
+                show_default_view();
                 return false;
             }
             const narrow_opts = {
@@ -152,6 +155,8 @@ function do_hashchange_normal(from_reload) {
         }
         case "":
         case "#":
+            show_default_view();
+            break;
         case "#recent_topics":
             recent_topics.show();
             break;
@@ -176,6 +181,9 @@ function do_hashchange_overlay(old_hash) {
     if (old_hash === undefined) {
         // User directly requested to open an overlay.
         // We need to show recent topics in the background.
+        // Even though recent topics may not be the default view
+        // here, we show it because we need to show a view in
+        // background and recent topics seems preferrable for that.
         recent_topics.show();
     }
     const base = hash_util.get_hash_category(window.location.hash);
diff --git a/static/js/hotkey.js b/static/js/hotkey.js
index dd3d0c24cc..3ba548f271 100644
--- a/static/js/hotkey.js
+++ b/static/js/hotkey.js
@@ -305,7 +305,7 @@ export function process_escape_key(e) {
         return true;
     }
 
-    hashchange.go_to_location("");
+    hashchange.show_default_view();
     return true;
 }
 
@@ -770,7 +770,7 @@ export function process_hotkey(e, hotkey) {
             narrow.narrow_to_next_pm_string();
             return true;
         case "open_recent_topics":
-            hashchange.go_to_location("#");
+            hashchange.go_to_location("#recent_topics");
             return true;
         case "all_messages":
             hashchange.go_to_location("#all_messages");
diff --git a/static/js/recent_topics.js b/static/js/recent_topics.js
index a29c4ca6d9..a0ae0e9ce4 100644
--- a/static/js/recent_topics.js
+++ b/static/js/recent_topics.js
@@ -564,10 +564,13 @@ export function change_focused_element(e, input_key) {
         }
 
         switch (input_key) {
+            //  Allow broswer to handle all
+            //  character keypresses.
             case "vim_left":
             case "vim_right":
             case "vim_down":
             case "vim_up":
+            case "open_recent_topics":
                 return false;
             case "shift_tab":
                 current_focus_elem = filter_buttons().last();
diff --git a/static/js/server_events_dispatch.js b/static/js/server_events_dispatch.js
index 51154249b9..c6441ac230 100644
--- a/static/js/server_events_dispatch.js
+++ b/static/js/server_events_dispatch.js
@@ -445,6 +445,7 @@ export function dispatch_normal_event(event) {
             const user_display_settings = [
                 "color_scheme",
                 "default_language",
+                "default_view",
                 "demote_inactive_streams",
                 "dense_mode",
                 "emojiset",
diff --git a/static/js/settings.js b/static/js/settings.js
index 7d8449912d..9bd32fc75d 100644
--- a/static/js/settings.js
+++ b/static/js/settings.js
@@ -83,6 +83,7 @@ export function build_page() {
         settings_label,
         demote_inactive_streams_values: settings_config.demote_inactive_streams_values,
         color_scheme_values: settings_config.color_scheme_values,
+        default_view_values: settings_config.default_view_values,
         twenty_four_hour_time_values: settings_config.twenty_four_hour_time_values,
         general_settings: settings_config.all_notifications().general_settings,
         notification_settings: settings_config.all_notifications().settings,
diff --git a/static/js/settings_config.js b/static/js/settings_config.js
index 95a19bff41..6cb7047d7d 100644
--- a/static/js/settings_config.js
+++ b/static/js/settings_config.js
@@ -28,6 +28,17 @@ export const demote_inactive_streams_values = {
     },
 };
 
+export const default_view_values = {
+    recent_topics: {
+        code: "recent_topics",
+        description: i18n.t("Recent topics"),
+    },
+    all_messages: {
+        code: "all_messages",
+        description: i18n.t("All messages"),
+    },
+};
+
 export const color_scheme_values = {
     automatic: {
         code: 1,
diff --git a/static/js/settings_display.js b/static/js/settings_display.js
index 6c8ef5bffb..cd68a80f11 100644
--- a/static/js/settings_display.js
+++ b/static/js/settings_display.js
@@ -42,6 +42,8 @@ export function set_up() {
 
     $("#color_scheme").val(page_params.color_scheme);
 
+    $("#default_view").val(page_params.default_view);
+
     $("#twenty_four_hour_time").val(JSON.stringify(page_params.twenty_four_hour_time));
 
     $(`.emojiset_choice[value="${CSS.escape(page_params.emojiset)}"]`).prop("checked", true);
@@ -109,6 +111,11 @@ export function set_up() {
         change_display_setting(data, "#display-settings-status");
     });
 
+    $("#default_view").on("change", function () {
+        const data = {default_view: JSON.stringify(this.value)};
+        change_display_setting(data, "#display-settings-status");
+    });
+
     $("body").on("click", ".reload_link", () => {
         window.location.reload();
     });
@@ -179,6 +186,7 @@ export function update_page() {
     $("#translate_emoticons").prop("checked", page_params.translate_emoticons);
     $("#twenty_four_hour_time").val(JSON.stringify(page_params.twenty_four_hour_time));
     $("#color_scheme").val(JSON.stringify(page_params.color_scheme));
+    $("#default_view").val(page_params.default_view);
 
     // TODO: Set emojiset selector here.
     // Longer term, we'll want to automate this function
diff --git a/static/templates/settings/display_settings.hbs b/static/templates/settings/display_settings.hbs
index 2cca09ebef..0a90eea539 100644
--- a/static/templates/settings/display_settings.hbs
+++ b/static/templates/settings/display_settings.hbs
@@ -21,6 +21,15 @@
             
{{t "Display settings" }}
             
 
+            
+                
+                
+            
+
             
                 
diff --git a/templates/zerver/api/changelog.md b/templates/zerver/api/changelog.md
index f6ca084132..fa4c28e33f 100644
--- a/templates/zerver/api/changelog.md
+++ b/templates/zerver/api/changelog.md
@@ -10,14 +10,19 @@ below features are supported.
 
 ## Changes in Zulip 4.0
 
+**Feature level 42**
+
+* `PATCH /settings/display`: Added a new `default_view` setting allowing
+  the user to [set the default view](/help/change-default-view).
+
 **Feature level 41**
 
-* [`GET /events`](/api/get-events): Remove name field from update
+* [`GET /events`](/api/get-events): Removed `name` field from update
   subscription events.
 
 **Feature level 40**
 
-* [`GET /events`](/api/get-events): Remove email field from update
+* [`GET /events`](/api/get-events): Removed `email` field from update
   subscription events.
 
 **Feature level 39**
diff --git a/templates/zerver/app/keyboard_shortcuts.html b/templates/zerver/app/keyboard_shortcuts.html
index f2b1885148..563ed94680 100644
--- a/templates/zerver/app/keyboard_shortcuts.html
+++ b/templates/zerver/app/keyboard_shortcuts.html
@@ -52,6 +52,10 @@
                     
{% trans %}Show keyboard shortcuts{% endtrans %} | 
                     ? | 
                 
+                
+                    | {% trans %}Go to default view{% endtrans %} | 
+                    Esc or Ctrl + [ | 
+                
             
         
         
@@ -241,7 +245,7 @@
                 
                 
                     | {% trans %}View recent topics{% endtrans %} | 
-                    T or Esc or Ctrl + [ | 
+                    T | 
                 
                 
                     | {% trans %}Search recent topics{% endtrans %} | 
diff --git a/templates/zerver/app/left_sidebar.html b/templates/zerver/app/left_sidebar.html
index ef55ff9ef0..199edfc9fe 100644
--- a/templates/zerver/app/left_sidebar.html
+++ b/templates/zerver/app/left_sidebar.html
@@ -57,7 +57,7 @@
                 
             
             
-                
+                
                     
                         
                     
diff --git a/templates/zerver/help/change-default-view.md b/templates/zerver/help/change-default-view.md
new file mode 100644
index 0000000000..cb6f72deba
--- /dev/null
+++ b/templates/zerver/help/change-default-view.md
@@ -0,0 +1,26 @@
+# Change default view
+
+The default view in Zulip (i.e. what view you reach after logging in
+to the Zulip webapp or hitting the `Esc` keyboard shortcut repeatedly)
+can be configured.  By default, **Recent topics** is the default view;
+the previous default, **All messages**, is also supported.
+
+[Contact us](/help/contact-support) if you'd like to be able to
+configure a different view as the default.
+
+### Change default view
+
+{start_tabs}
+
+{settings_tab|display-settings}
+
+2. Under **Display Settings**, click on the **Default view** dropdown.
+
+3. Select a view.
+
+4. Open a new Zulip tab or press `Esc` twice (first to exit
+   "Settings", and again to return to the default view) to see your
+   changes in action.
+
+{end_tabs}
+
diff --git a/templates/zerver/help/include/sidebar_index.md b/templates/zerver/help/include/sidebar_index.md
index 900c3170ec..43df40f886 100644
--- a/templates/zerver/help/include/sidebar_index.md
+++ b/templates/zerver/help/include/sidebar_index.md
@@ -14,6 +14,7 @@
 * [Review your settings](/help/review-your-settings)
 * [Change your profile picture](/help/change-your-profile-picture)
 * [Change your language](/help/change-your-language)
+* [Change default view](/help/change-default-view)
 * [Use 24-hour time](/help/change-the-time-format)
 * [Joining an organization](/help/join-a-zulip-organization)
 * [Switching between organizations](/help/switching-between-organizations)
diff --git a/templates/zerver/help/keyboard-shortcuts.md b/templates/zerver/help/keyboard-shortcuts.md
index 30cd989ca0..ade8419e60 100644
--- a/templates/zerver/help/keyboard-shortcuts.md
+++ b/templates/zerver/help/keyboard-shortcuts.md
@@ -41,6 +41,9 @@ below, and add more to your repertoire as needed.
 
 * **Toggle keyboard shortcuts view**: `?`
 
+* **Go to default view**: Press `Esc` or `Ctrl + [` until you are in
+  the [default view](/help/change-default-view).
+
 ## Navigation
 
 * **Search messages**: `/` or `Ctrl+k`
@@ -139,9 +142,9 @@ title="thumbs up"/>**: `+`
 
 ## Recent topics
 
-* **View recent topics**: `t` or `Esc` or `Ctrl` + `[`
+* **View recent topics**: `t`
 * **Search recent topics**: `t`
-* **Escape from recent topics search**: `esc` or arrow keys
+* **Escape from recent topics search**: `Esc` or arrow keys
 * **Navigate recent topics**: Use arrow keys or vim keys (`j`, `k`, `l`, `h`).
 
 Use `Enter` to engage with elements.
diff --git a/version.py b/version.py
index 3640520e64..b04a7e4c68 100644
--- a/version.py
+++ b/version.py
@@ -30,7 +30,7 @@ DESKTOP_WARNING_VERSION = "5.2.0"
 #
 # Changes should be accompanied by documentation explaining what the
 # new level means in templates/zerver/api/changelog.md.
-API_FEATURE_LEVEL = 41
+API_FEATURE_LEVEL = 42
 
 # Bump the minor PROVISION_VERSION to indicate that folks should provision
 # only when going from an old version of the code to a newer version. Bump
@@ -45,4 +45,4 @@ API_FEATURE_LEVEL = 41
 #   historical commits sharing the same major version, in which case a
 #   minor version bump suffices.
 
-PROVISION_VERSION = "132.0"
+PROVISION_VERSION = "133.0"
diff --git a/zerver/migrations/0311_userprofile_default_view.py b/zerver/migrations/0311_userprofile_default_view.py
new file mode 100644
index 0000000000..89182a8a74
--- /dev/null
+++ b/zerver/migrations/0311_userprofile_default_view.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.7 on 2021-03-10 04:14
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("zerver", "0310_jsonfield"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="userprofile",
+            name="default_view",
+            field=models.TextField(default="recent_topics"),
+        ),
+    ]
diff --git a/zerver/models.py b/zerver/models.py
index 72326706f6..b472caf208 100644
--- a/zerver/models.py
+++ b/zerver/models.py
@@ -1153,6 +1153,9 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
 
     # display settings
     default_language: str = models.CharField(default="en", max_length=MAX_LANGUAGE_ID_LENGTH)
+    # This setting controls which view is rendered first when Zulip loads.
+    # Values for it are URL suffix after `#`.
+    default_view: str = models.TextField(default="recent_topics")
     dense_mode: bool = models.BooleanField(default=True)
     fluid_layout_width: bool = models.BooleanField(default=False)
     high_contrast_mode: bool = models.BooleanField(default=False)
@@ -1244,6 +1247,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
     property_types = dict(
         color_scheme=int,
         default_language=str,
+        default_view=str,
         demote_inactive_streams=int,
         dense_mode=bool,
         emojiset=str,
diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml
index bc406e995f..09fc9094d4 100644
--- a/zerver/openapi/zulip.yaml
+++ b/zerver/openapi/zulip.yaml
@@ -6725,6 +6725,15 @@ paths:
                           Present if `update_display_settings` is present in `fetch_event_types`.
 
                           Whether the user has chosen for the layout width to be fluid.
+                      default_view:
+                        type: string
+                        description: |
+                          Present if `update_display_settings` is present in `fetch_event_types`.
+
+                          The [default view](/help/change-default-view) in Zulip, represented
+                          as the URL suffix after `#` to be rendered when Zulip loads.
+
+                          Currently supported values are `all_messages` and `recent_topics`.
                       high_contrast_mode:
                         type: boolean
                         description: |
diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py
index dfecda37ec..9ef59c2d50 100644
--- a/zerver/tests/test_events.py
+++ b/zerver/tests/test_events.py
@@ -1863,6 +1863,7 @@ class UserDisplayActionTest(BaseAction):
         test_changes: Dict[str, Any] = dict(
             emojiset=["twitter"],
             default_language=["es", "de", "en"],
+            default_view=["all_messages", "recent_topics"],
             timezone=["America/Denver", "Pacific/Pago_Pago", "Pacific/Galapagos", ""],
             demote_inactive_streams=[2, 3, 1],
             color_scheme=[2, 3, 1],
diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py
index 4c063428c7..461450ecdf 100644
--- a/zerver/tests/test_home.py
+++ b/zerver/tests/test_home.py
@@ -53,6 +53,7 @@ class HomeTest(ZulipTestCase):
         "debug_mode",
         "default_language",
         "default_language_name",
+        "default_view",
         "delivery_email",
         "demote_inactive_streams",
         "dense_mode",
diff --git a/zerver/tests/test_settings.py b/zerver/tests/test_settings.py
index 9d4801d76b..4f8b78af3d 100644
--- a/zerver/tests/test_settings.py
+++ b/zerver/tests/test_settings.py
@@ -336,6 +336,7 @@ class ChangeSettingsTest(ZulipTestCase):
 
         test_changes: Dict[str, Any] = dict(
             default_language="de",
+            default_view="all_messages",
             emojiset="google",
             timezone="US/Mountain",
             demote_inactive_streams=2,
diff --git a/zerver/tests/test_users.py b/zerver/tests/test_users.py
index 37282388d4..e60ba27c83 100644
--- a/zerver/tests/test_users.py
+++ b/zerver/tests/test_users.py
@@ -1183,6 +1183,7 @@ class UserProfileTest(ZulipTestCase):
         hamlet.color_scheme = UserProfile.COLOR_SCHEME_LIGHT
 
         cordelia.default_language = "de"
+        cordelia.default_view = "all_messages"
         cordelia.emojiset = "twitter"
         cordelia.timezone = "America/Phoenix"
         cordelia.color_scheme = UserProfile.COLOR_SCHEME_NIGHT
diff --git a/zerver/views/user_settings.py b/zerver/views/user_settings.py
index 665e7f0e93..ae15e90de7 100644
--- a/zerver/views/user_settings.py
+++ b/zerver/views/user_settings.py
@@ -180,6 +180,7 @@ def json_change_settings(
 
 
 emojiset_choices = {emojiset["key"] for emojiset in UserProfile.emojiset_choices()}
+default_view_options = ["recent_topics", "all_messages"]
 
 
 @human_users_only
@@ -197,6 +198,9 @@ def update_display_settings_backend(
     ),
     translate_emoticons: Optional[bool] = REQ(validator=check_bool, default=None),
     default_language: Optional[str] = REQ(validator=check_string, default=None),
+    default_view: Optional[str] = REQ(
+        validator=check_string_in(default_view_options), default=None
+    ),
     left_side_userlist: Optional[bool] = REQ(validator=check_bool, default=None),
     emojiset: Optional[str] = REQ(validator=check_string_in(emojiset_choices), default=None),
     demote_inactive_streams: Optional[int] = REQ(